logster 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -19
  3. data/.rubocop.yml +1 -1
  4. data/.travis.yml +16 -16
  5. data/CHANGELOG.md +169 -166
  6. data/Gemfile +4 -4
  7. data/Guardfile +8 -8
  8. data/LICENSE.txt +22 -22
  9. data/README.md +99 -99
  10. data/Rakefile +21 -21
  11. data/assets/fonts/FontAwesome.otf +0 -0
  12. data/assets/fonts/fontawesome-webfont.eot +0 -0
  13. data/assets/fonts/fontawesome-webfont.svg +639 -639
  14. data/assets/fonts/fontawesome-webfont.ttf +0 -0
  15. data/assets/fonts/fontawesome-webfont.woff +0 -0
  16. data/assets/fonts/fontawesome-webfont.woff2 +0 -0
  17. data/assets/images/Icon-144_rounded.png +0 -0
  18. data/assets/images/Icon-144_square.png +0 -0
  19. data/assets/images/icon_144x144.png +0 -0
  20. data/assets/images/icon_64x64.png +0 -0
  21. data/assets/javascript/client-app.js +100 -100
  22. data/assets/stylesheets/client-app.css +0 -0
  23. data/build_client_app.sh +0 -0
  24. data/client-app/.editorconfig +20 -20
  25. data/client-app/.ember-cli +9 -9
  26. data/client-app/.eslintignore +19 -19
  27. data/client-app/.eslintrc.js +46 -46
  28. data/client-app/.gitignore +23 -23
  29. data/client-app/.travis.yml +27 -27
  30. data/client-app/.watchmanconfig +3 -3
  31. data/client-app/README.md +57 -57
  32. data/client-app/app/app.js +14 -14
  33. data/client-app/app/components/actions-menu.js +37 -37
  34. data/client-app/app/components/env-tab.js +44 -44
  35. data/client-app/app/components/message-info.js +65 -65
  36. data/client-app/app/components/message-row.js +45 -45
  37. data/client-app/app/components/panel-resizer.js +74 -74
  38. data/client-app/app/components/tab-contents.js +27 -27
  39. data/client-app/app/components/tab-link.js +5 -5
  40. data/client-app/app/components/tabbed-section.js +32 -32
  41. data/client-app/app/components/time-formatter.js +25 -25
  42. data/client-app/app/components/update-time.js +21 -21
  43. data/client-app/app/controllers/index.js +105 -105
  44. data/client-app/app/controllers/show.js +13 -13
  45. data/client-app/app/index.html +29 -29
  46. data/client-app/app/initializers/app-init.js +72 -72
  47. data/client-app/app/lib/preload.js +14 -14
  48. data/client-app/app/lib/utilities.js +140 -140
  49. data/client-app/app/models/message-collection.js +178 -178
  50. data/client-app/app/models/message.js +100 -100
  51. data/client-app/app/resolver.js +3 -3
  52. data/client-app/app/router.js +14 -14
  53. data/client-app/app/routes/index.js +57 -57
  54. data/client-app/app/routes/show.js +14 -14
  55. data/client-app/app/styles/app.css +521 -521
  56. data/client-app/app/templates/application.hbs +2 -2
  57. data/client-app/app/templates/components/actions-menu.hbs +12 -12
  58. data/client-app/app/templates/components/env-tab.hbs +10 -10
  59. data/client-app/app/templates/components/message-info.hbs +41 -41
  60. data/client-app/app/templates/components/message-row.hbs +15 -15
  61. data/client-app/app/templates/components/panel-resizer.hbs +3 -3
  62. data/client-app/app/templates/components/tabbed-section.hbs +10 -10
  63. data/client-app/app/templates/components/time-formatter.hbs +1 -1
  64. data/client-app/app/templates/index.hbs +58 -58
  65. data/client-app/app/templates/show.hbs +7 -7
  66. data/client-app/config/environment.js +51 -51
  67. data/client-app/config/optional-features.json +3 -3
  68. data/client-app/config/targets.js +18 -18
  69. data/client-app/ember-cli-build.js +29 -29
  70. data/client-app/package-lock.json +11365 -11365
  71. data/client-app/package.json +56 -56
  72. data/client-app/testem.js +25 -25
  73. data/client-app/tests/index.html +34 -34
  74. data/client-app/tests/integration/components/actions-menu-test.js +26 -26
  75. data/client-app/tests/integration/components/env-tab-test.js +73 -73
  76. data/client-app/tests/integration/components/message-info-test.js +26 -26
  77. data/client-app/tests/integration/components/message-row-test.js +26 -26
  78. data/client-app/tests/integration/components/panel-resizer-test.js +26 -26
  79. data/client-app/tests/integration/components/tab-contents-test.js +26 -26
  80. data/client-app/tests/integration/components/tab-link-test.js +26 -26
  81. data/client-app/tests/integration/components/tabbed-section-test.js +26 -26
  82. data/client-app/tests/integration/components/time-formatter-test.js +26 -26
  83. data/client-app/tests/integration/components/update-time-test.js +26 -26
  84. data/client-app/tests/test-helper.js +8 -8
  85. data/client-app/tests/unit/controllers/index-test.js +12 -12
  86. data/client-app/tests/unit/controllers/show-test.js +12 -12
  87. data/client-app/tests/unit/initializers/app-init-test.js +31 -31
  88. data/client-app/tests/unit/routes/index-test.js +11 -11
  89. data/client-app/tests/unit/routes/show-test.js +11 -11
  90. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  91. data/lib/logster.rb +54 -54
  92. data/lib/logster/base_store.rb +141 -141
  93. data/lib/logster/configuration.rb +25 -25
  94. data/lib/logster/defer_logger.rb +14 -14
  95. data/lib/logster/ignore_pattern.rb +65 -65
  96. data/lib/logster/logger.rb +113 -113
  97. data/lib/logster/message.rb +212 -212
  98. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  99. data/lib/logster/middleware/reporter.rb +55 -55
  100. data/lib/logster/middleware/viewer.rb +221 -221
  101. data/lib/logster/rails/railtie.rb +63 -63
  102. data/lib/logster/redis_store.rb +566 -566
  103. data/lib/logster/scheduler.rb +54 -40
  104. data/lib/logster/version.rb +3 -3
  105. data/lib/logster/web.rb +14 -14
  106. data/logster.gemspec +35 -35
  107. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  108. data/test/fake_data/Gemfile +4 -4
  109. data/test/fake_data/generate.rb +10 -10
  110. data/test/logster/middleware/test_reporter.rb +19 -19
  111. data/test/logster/middleware/test_viewer.rb +96 -96
  112. data/test/logster/test_base_store.rb +147 -147
  113. data/test/logster/test_defer_logger.rb +34 -34
  114. data/test/logster/test_ignore_pattern.rb +41 -41
  115. data/test/logster/test_logger.rb +86 -86
  116. data/test/logster/test_message.rb +119 -119
  117. data/test/logster/test_redis_rate_limiter.rb +230 -230
  118. data/test/logster/test_redis_store.rb +720 -720
  119. data/test/test_helper.rb +38 -38
  120. data/vendor/assets/javascripts/logster.js.erb +39 -39
  121. metadata +2 -2
@@ -1,720 +1,720 @@
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
- env = { test_env: "this is env" }
18
- msg = @store.report(Logger::WARN, "test", "testing", env: env)
19
- @store.delete(msg)
20
- latest = @store.latest
21
-
22
- assert_equal(0, latest.length)
23
- assert_nil(@store.get_env(msg.key))
24
- end
25
-
26
- def test_latest
27
- @store.report(Logger::WARN, "test", "IGNORE")
28
- @store.report(Logger::WARN, "test", "This is a warning")
29
- @store.report(Logger::WARN, "test", "This is another warning")
30
-
31
- latest = @store.latest(limit: 2)
32
-
33
- assert_equal(2, latest.length)
34
- assert_equal("This is a warning", latest[0].message)
35
- assert_equal("This is another warning", latest[1].message)
36
- assert_equal(Logger::WARN, latest[1].severity)
37
- assert_equal("test", latest[1].progname)
38
- assert(!latest[1].key.nil?)
39
- end
40
-
41
- def test_latest_after
42
- 10.times do |i|
43
- @store.report(Logger::WARN, "test", "A#{i}")
44
- end
45
-
46
- message = @store.latest[-1]
47
-
48
- 3.times do |i|
49
- @store.report(Logger::WARN, "test", i.to_s)
50
- end
51
-
52
- message = @store.latest(after: message.key, limit: 3)[0]
53
-
54
- assert_equal("0", message.message)
55
- end
56
-
57
- def test_latest_before
58
- 10.times do
59
- @store.report(Logger::WARN, "test", "A")
60
- end
61
- 10.times do
62
- @store.report(Logger::WARN, "test", "B")
63
- end
64
- 10.times do
65
- @store.report(Logger::WARN, "test", "C")
66
- end
67
-
68
- messages = @store.latest(limit: 10)
69
- assert_equal("C", messages[0].message)
70
- assert_equal(10, messages.length)
71
-
72
- messages = @store.latest(limit: 10, before: messages[0].key)
73
- assert_equal("B", messages[0].message)
74
- assert_equal(10, messages.length)
75
-
76
- messages = @store.latest(limit: 10, before: messages[0].key)
77
- assert_equal("A", messages[0].message)
78
- assert_equal(10, messages.length)
79
-
80
- end
81
-
82
- def test_get
83
- a_env = { "a_message" => "A MESSAGE" }
84
- a_message = @store.report(Logger::WARN, "test", "A", env: a_env)
85
- b_message = @store.report(Logger::WARN, "test", "B")
86
- @store.report(Logger::WARN, "test", "C")
87
-
88
- a_message = @store.get(a_message.key)
89
- assert_equal("A", a_message.message)
90
- assert_equal("B", b_message.message)
91
- assert(a_env <= a_message.env)
92
-
93
- a_message = @store.get(a_message.key, load_env: false)
94
- assert_equal("A", a_message.message)
95
- assert_nil(a_message.env)
96
- end
97
-
98
- def test_save_saves_env_separately
99
- env = { "myenv" => "thisisenv" }
100
- message = @store.report(Logger::WARN, "test", "title", env: env)
101
- message = @store.get(message.key, load_env: false)
102
- assert_nil(message.env)
103
-
104
- message = @store.get(message.key)
105
- assert(env <= message.env)
106
-
107
- assert(env <= @store.get_env(message.key))
108
- end
109
-
110
- def test_bulk_get
111
- keys = []
112
-
113
- 5.times do |n|
114
- env = n == 0 ? nil : { "test_#{n}" => "envsss" }
115
- keys << @store.report(Logger::WARN, "progname", "test_#{n}", env: env).key
116
- end
117
-
118
- messages = @store.bulk_get(keys)
119
-
120
- 5.times do |n|
121
- msg = messages[n]
122
- assert_equal("test_#{n}", msg.message)
123
- if n == 0
124
- assert_equal(Logster::Message.default_env, msg.env)
125
- else
126
- assert({ "test_#{n}" => "envsss" } <= msg.env)
127
- end
128
- end
129
- end
130
-
131
- def test_get_env
132
- env = { "my_little_env" => "some value" }
133
- message = @store.report(Logger::WARN, "test", "A", env: env)
134
- assert(env <= @store.get_env(message.key))
135
- assert_nil(@store.get_env("nonexistentkey"))
136
- end
137
-
138
- def test_replace_and_bump
139
- old_env = { "old_env" => "old value" }
140
- message = @store.report(Logger::WARN, "test", "A", env: old_env)
141
-
142
- unsaved_env = { "unsaved_env" => "lost value" }
143
- message.env = unsaved_env
144
-
145
- @store.replace_and_bump(message, save_env: false)
146
-
147
- message = @store.get(message.key)
148
- assert(old_env <= message.env)
149
- refute(unsaved_env <= message.env)
150
-
151
- saved_env = { "saved_env" => "saved value!" }
152
- message.env = saved_env
153
-
154
- @store.replace_and_bump(message)
155
-
156
- message = @store.get(message.key)
157
- assert(saved_env == message.env)
158
- end
159
-
160
- def test_backward_compatibility_no_loss_of_data
161
- # previously we were storing env samples as a part of the main message json
162
- # now we've switched to storing samples separately from the main message
163
- # we need to make we don't lose env data of messages stored the old way
164
- # when we migrate to the new system
165
-
166
- # it probably makes sense to remove this test after a while (say 6-12 months)
167
-
168
- Logster.config.allow_grouping = true
169
- backtrace = "fake backtrace"
170
- env = { "some_env" => "some env" }
171
- message = Logster::Message.new(Logger::WARN, "", "title", count: 60)
172
- message.env = env
173
- message.backtrace = backtrace
174
-
175
- @store.save(message)
176
-
177
- # hack to force env to be stored with the main message json
178
- @store.redis.hset(@store.send("hash_key"), message.key, message.to_json(exclude_env: false))
179
-
180
- another_env = { "another_env" => "more env" }
181
- message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: another_env)
182
- message = @store.get(message.key)
183
-
184
- assert(env <= message.env)
185
- assert_equal(61, message.count)
186
- # another_env is not merged cause count is 60, only the count is updated
187
-
188
- # make sure we are now storing env samples separately
189
- message = @store.get(message.key, load_env: false)
190
- assert_nil(message.env)
191
- ensure
192
- Logster.config.allow_grouping = false
193
- end
194
-
195
- def test_backward_compatibility_no_loss_of_data_2
196
- # same story as the test above, just a bit different
197
-
198
- Logster.config.allow_grouping = true
199
- backtrace = "fake backtrace"
200
- env = { "some_env" => "some env" }
201
- message = Logster::Message.new(Logger::WARN, "", "title")
202
- message.env = env
203
- message.backtrace = backtrace
204
-
205
- @store.save(message)
206
-
207
- # hack to force env to be stored with the main message json
208
- @store.redis.hset(@store.send("hash_key"), message.key, message.to_json(exclude_env: false))
209
-
210
- another_env = { "another_env" => "more env" }
211
- message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: another_env)
212
- message = @store.get(message.key)
213
-
214
- assert_instance_of(Array, message.env)
215
- assert(env <= message.env[0])
216
- assert(another_env <= message.env[1])
217
- assert_equal(2, message.env.size)
218
- assert_equal(2, message.count)
219
-
220
- # make sure we are now storing env samples separately
221
- message = @store.get(message.key, load_env: false)
222
- assert_nil(message.env)
223
- ensure
224
- Logster.config.allow_grouping = false
225
- end
226
-
227
- def test_merging_performance
228
- Logster.config.allow_grouping = true
229
- backtrace = "fake backtrace"
230
- env = { "some_env" => "some env" }
231
- another_env = { "another_env" => "more env" }
232
- yet_another_env = { "moaar_env" => "more env" }
233
-
234
- @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: env, count: 49)
235
-
236
- message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: another_env)
237
- assert_instance_of(Array, message.env)
238
- assert_equal(2, message.env.size)
239
- assert(env <= message.env[0])
240
- assert(another_env <= message.env[1])
241
-
242
- message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: yet_another_env)
243
- # we don't need to load env from redis cause we don't
244
- # need to merge new env samples if count is 50 or more
245
- assert_nil(message.env)
246
- ensure
247
- Logster.config.allow_grouping = false
248
- end
249
-
250
- def test_backlog
251
- env = { "backlog_test" => "BACKLOG" }
252
- @store.max_backlog = 1
253
- deleted_msg = @store.report(Logger::WARN, "test", "A")
254
- @store.report(Logger::WARN, "test", "A")
255
- @store.report(Logger::WARN, "test", "A")
256
- @store.report(Logger::WARN, "test", "B", env: env)
257
-
258
- latest = @store.latest
259
-
260
- assert_equal(1, latest.length)
261
- assert_equal("B", latest[0].message)
262
- assert(env <= latest[0].env)
263
- assert_nil(@store.get(deleted_msg.key))
264
- assert_nil(@store.get_env(deleted_msg.key))
265
- end
266
-
267
- def test_save_unsave
268
- @store.max_backlog = 3
269
- @store.report(Logger::WARN, "test", "A")
270
- b_message = @store.report(Logger::WARN, "test", "B")
271
- @store.protect b_message.key
272
- c_message = @store.report(Logger::WARN, "test", "C")
273
- @store.protect c_message.key
274
- @store.report(Logger::WARN, "test", "D")
275
-
276
- latest = @store.latest
277
-
278
- assert_equal(3, latest.length)
279
- assert_equal("B", latest[0].message)
280
- assert_equal("C", latest[1].message)
281
- assert_equal(true, latest[1].protected)
282
- assert_equal("D", latest[2].message)
283
-
284
- # Saved messages still accessible by key
285
- assert_equal("B", @store.get(b_message.key).message)
286
- assert_equal(true, @store.get(b_message.key).protected)
287
-
288
- # Unsave does not delete message if still recent
289
- @store.unprotect c_message.key
290
- assert_equal("C", @store.get(c_message.key).message)
291
- assert_equal(false, @store.get(c_message.key).protected)
292
- end
293
-
294
- def test_clear
295
- env = { "clear_env" => "cllleear" }
296
- @store.max_backlog = 25
297
- a_message = @store.report(Logger::WARN, "test", "A", timestamp: Time.now - (24 * 60 * 60), env: env)
298
- @store.protect a_message.key
299
- 20.times do
300
- @store.report(Logger::WARN, "test", "B", env: env)
301
- end
302
- c_message = @store.report(Logger::WARN, "test", "C", timestamp: Time.now + (24 * 60 * 60), env: env)
303
- @store.protect c_message.key
304
- d_message = @store.report(Logger::WARN, "test", "D", env: env)
305
- 10.times do
306
- @store.report(Logger::WARN, "test", "E", env: env)
307
- end
308
-
309
- latest = @store.latest
310
- assert_equal(25, latest.length)
311
-
312
- @store.clear
313
-
314
- # Protected messages are still accessible by their key
315
- assert_equal("C", @store.get(c_message.key).message)
316
- assert(env <= @store.get_env(c_message.key))
317
- # Unprotected messages are gone
318
- assert_nil(@store.get(d_message.key))
319
- assert_nil(@store.get_env(d_message.key))
320
-
321
- # The latest list is rebuilt with protected messages, earliest first
322
- # Including messages that previously fell off (A)
323
- latest = @store.latest
324
- assert_equal(2, latest.length)
325
- assert_equal("A", latest[0].message)
326
- assert_equal("C", latest[1].message)
327
- assert(env <= latest[0].env)
328
- assert(env <= latest[1].env)
329
- end
330
-
331
- def test_hash_cleanup
332
- @store.max_backlog = 2
333
- a_message = @store.report(Logger::WARN, "test", "A")
334
- @store.report(Logger::WARN, "test", "B")
335
- @store.report(Logger::WARN, "test", "C")
336
-
337
- assert_nil(@store.get(a_message.key))
338
- end
339
-
340
- def test_filter_latest
341
- @store.report(Logger::INFO, "test", "A")
342
- @store.report(Logger::WARN, "test", "B")
343
-
344
- messages = @store.latest
345
- assert_equal(2, messages.length)
346
-
347
- messages = @store.latest(after: messages.last.key)
348
- assert_equal(0, messages.length)
349
-
350
- 10.times do
351
- @store.report(Logger::INFO, "test", "A")
352
- end
353
- @store.report(Logger::ERROR, "test", "C")
354
- 10.times do
355
- @store.report(Logger::INFO, "test", "A")
356
- end
357
-
358
- latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2)
359
-
360
- assert_equal(2, latest.length)
361
- assert_equal("B", latest[0].message)
362
- assert_equal("C", latest[1].message)
363
-
364
- @store.report(Logger::ERROR, "test", "E")
365
- # respects after
366
- latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2, after: latest[1].key)
367
- assert_equal(1, latest.length)
368
- end
369
-
370
- def test_search
371
- @store.report(Logger::INFO, "test", "ABCDEFG")
372
- @store.report(Logger::INFO, "test", "TUVWXYZ")
373
-
374
- messages = @store.latest
375
- assert_equal(2, messages.length)
376
-
377
- latest = @store.latest(search: "TUVWXYZ")
378
-
379
- assert_equal(1, latest.length)
380
- assert_equal("TUVWXYZ", latest[0].message)
381
- end
382
-
383
- def test_search_exclude_results
384
- @store.report(Logger::INFO, "test", "ABCDEFG")
385
- @store.report(Logger::INFO, "test", "TUVWXYZ")
386
-
387
- messages = @store.latest
388
- assert_equal(2, messages.length)
389
-
390
- latest = @store.latest(search: "-ABCD")
391
-
392
- assert_equal(1, latest.length)
393
- assert_equal("TUVWXYZ", latest[0].message)
394
- end
395
-
396
- def test_regex_search
397
- @store.report(Logger::INFO, "test", "pattern_1")
398
- @store.report(Logger::INFO, "test", "pattern_2")
399
-
400
- messages = @store.latest
401
- assert_equal(2, messages.length)
402
-
403
- latest = @store.latest(search: /^pattern_[1]$/)
404
-
405
- assert_equal(1, latest.length)
406
- end
407
-
408
- def test_env_search
409
- @store.report(Logger::INFO, "test", "message ABCD", env: { cluster: "business5" })
410
- @store.report(Logger::INFO, "test", "message WXYZ", env: { cluster: "business7" })
411
-
412
- messages = @store.latest
413
- assert_equal(2, messages.length)
414
-
415
- latest = @store.latest(search: "business5")
416
-
417
- assert_equal(1, latest.length)
418
- assert_equal("message ABCD", latest[0].message)
419
-
420
- latest = @store.latest(search: "-business5")
421
-
422
- assert_equal(1, latest.length)
423
- assert_equal("message WXYZ", latest[0].message)
424
-
425
- latest = @store.latest(search: /business/)
426
-
427
- assert_equal(2, latest.length)
428
- assert_equal(["message ABCD", "message WXYZ"], latest.map(&:message).sort)
429
- end
430
-
431
- def test_array_env_search_preserve_env
432
- m1_original_env = [{ cluster: "business5" }, { cluster: "standard3" }]
433
- m2_original_env = [{ cluster: "business2" }, { cluster: "standard7" }]
434
-
435
- @store.report(Logger::INFO, "test", "message ABCD", env: m1_original_env, count: 2)
436
- @store.report(Logger::INFO, "test", "message WXYZ", env: m2_original_env, count: 2)
437
-
438
- messages = @store.latest
439
- assert_equal(2, messages.length)
440
-
441
- m1_key = messages[0].key
442
- m2_key = messages[1].key
443
-
444
- messages = @store.latest(search: "business")
445
- assert_equal(2, messages.size)
446
-
447
- # any hashes that don't match should be stripped from the env
448
- # array but only temporarily until it's sent to the client
449
- # env array should remain untouched in redis memory
450
- assert_equal(["business5"], messages[0].env.map { |env| env["cluster"] })
451
- assert_equal(1, messages[0].count)
452
- assert_equal(["business2"], messages[1].env.map { |env| env["cluster"] })
453
- assert_equal(1, messages[1].count)
454
-
455
- m1 = @store.get(m1_key)
456
- m2 = @store.get(m2_key)
457
- # original env should preserved in redis memory
458
- assert_equal(["business5", "standard3"], m1.env.map { |env| env["cluster"] })
459
- assert_equal(["business2", "standard7"], m2.env.map { |env| env["cluster"] })
460
- end
461
-
462
- def test_both_env_and_title_match_search
463
- @store.report(Logger::INFO, "test", "message", env: [{ cluster: "business15" }])
464
- @store.report(Logger::INFO, "test", "message2", env: { cluster: "business15" })
465
-
466
- messages = @store.latest
467
- assert_equal(2, messages.size)
468
-
469
- messages = @store.latest(search: "-business15")
470
- assert_equal(0, messages.size)
471
- end
472
-
473
- def test_data_kept_intact_on_report_when_env_matches_an_ignore_pattern
474
- begin
475
- Logster.config.allow_grouping = true
476
- backtrace = caller
477
- message = @store.report(Logger::WARN, "", "my error", env: { whatever: "something", backtrace: backtrace })
478
-
479
- @store.ignore = [
480
- Logster::IgnorePattern.new("business")
481
- ]
482
- @store.report(Logger::WARN, "", "my error", env: { cluster: "business17", backtrace: backtrace })
483
-
484
- message = @store.get(message.key)
485
- assert(Array === message.env)
486
- assert_equal(2, message.env.size)
487
- # message2 shouldn't vanish even if
488
- # its env matches an ignore pattern
489
- # however it should be merged with message1
490
- assert_equal("business17", message.env[1]["cluster"])
491
- ensure
492
- # reset so it doesn't affect other tests
493
- @store.ignore = nil
494
- Logster.config.allow_grouping = false
495
- end
496
- end
497
-
498
- def test_array_env_negative_search
499
- @store.report(Logger::INFO, "test", "message ABCD", env: [{ cluster: "business5" }, { cluster: "standard3" }], count: 2)
500
- @store.report(Logger::INFO, "test", "message WXYZ", env: [{ cluster: "business2" }, { cluster: "standard7" }], count: 2)
501
-
502
- messages = @store.latest
503
- assert_equal(2, messages.length)
504
-
505
- messages = @store.latest(search: "-business")
506
- assert_equal(2, messages.size)
507
-
508
- assert_equal(["standard3"], messages[0].env.map { |env| env["cluster"] })
509
- assert_equal(1, messages[0].count)
510
- assert_equal(["standard7"], messages[1].env.map { |env| env["cluster"] })
511
- assert_equal(1, messages[1].count)
512
- end
513
-
514
- def test_negative_search_MUST_not_match_title_in_order_to_include_message
515
- @store.report(Logger::INFO, "test", "message ABCD", env: [{ cluster: "business5" }, { cluster: "standard3" }], count: 2)
516
-
517
- messages = @store.latest(search: "-ABCD")
518
- assert_equal(0, messages.size) # cause title has ABCD
519
- end
520
-
521
- def test_positive_search_looks_at_title_OR_env
522
- @store.report(Logger::INFO, "test", "message", env: [{ cluster: "business5 ABCDEFG" }, { cluster: "standard3" }], count: 2)
523
-
524
- messages = @store.latest(search: "ABCDEFG")
525
- assert_equal(1, messages.size)
526
- assert_equal(1, messages[0].env.size)
527
- assert_equal("business5 ABCDEFG", messages[0].env[0]["cluster"])
528
- end
529
-
530
- def test_backtrace
531
- @store.report(Logger::INFO, "test", "pattern_1")
532
- message = @store.latest(limit: 1).first
533
- assert_match("test_backtrace", message.backtrace)
534
- end
535
-
536
- def test_ignore
537
- @store.ignore = [/^test/]
538
- @store.report(Logger::INFO, "test", "test it")
539
- @store.report(Logger::INFO, "test", " test it")
540
-
541
- assert_equal(1, @store.latest.count)
542
- end
543
-
544
- def test_solve
545
- Logster.config.application_version = "abc"
546
-
547
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
548
- m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1")
549
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace2")
550
-
551
- assert_equal(3, @store.latest.count)
552
-
553
- @store.solve(m.key)
554
-
555
- assert_equal(1, @store.latest.count)
556
-
557
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
558
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
559
-
560
- assert_equal(2, @store.latest.count)
561
-
562
- ensure
563
- Logster.config.application_version = nil
564
- end
565
-
566
- def test_solve_grouped
567
- Logster.config.allow_grouping = true
568
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
569
- m = @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg" })
570
-
571
- assert_equal(1, @store.latest.count)
572
-
573
- @store.solve(m.key)
574
-
575
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
576
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg" })
577
-
578
- assert_equal(0, @store.latest.count)
579
-
580
- ensure
581
- Logster.config.allow_grouping = false
582
- end
583
-
584
- def test_clears_solved
585
- m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1", env: { "application_version" => "abc" })
586
- @store.solve(m.key)
587
-
588
- assert_equal(1, @store.solved.length)
589
-
590
- @store.clear
591
- assert_equal(0, @store.solved.length)
592
- end
593
-
594
- def test_solving_with_some_missing_version
595
-
596
- m = @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
597
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
598
-
599
- @store.solve(m.key)
600
-
601
- assert_equal(1, @store.latest.count)
602
- end
603
-
604
- def test_env
605
- env = Rack::MockRequest.env_for("/test").merge(
606
- "HTTP_HOST" => "www.site.com",
607
- "HTTP_USER_AGENT" => "SOME WHERE"
608
- )
609
- orig = env.dup
610
- orig["test"] = "tests"
611
- orig["test1"] = "tests1"
612
- Logster.add_to_env(env, "test", "tests")
613
- Logster.add_to_env(env, "test1", "tests1")
614
-
615
- orig.delete_if do |k, v|
616
- !%w{
617
- HTTP_HOST
618
- REQUEST_METHOD
619
- HTTP_USER_AGENT
620
- test
621
- test1
622
- }.include? k
623
- end
624
-
625
- @store.report(Logger::INFO, "test", "test", env: env)
626
-
627
- env = @store.latest.last.env
628
-
629
- env.delete "hostname"
630
- env.delete "process_id"
631
-
632
- assert_equal(orig, env)
633
- end
634
-
635
- def test_rate_limits
636
- %w{minute hour}.each do |duration|
637
- begin
638
- called = false
639
-
640
- assert_instance_of(
641
- Logster::RedisRateLimiter,
642
- @store.public_send("register_rate_limit_per_#{duration}", Logger::WARN, 0) do
643
- called = true
644
- end
645
- )
646
-
647
- @store.report(Logger::WARN, "test", "test")
648
- assert called
649
- ensure
650
- reset_redis
651
- end
652
- end
653
- end
654
-
655
- def test_rate_limits_only_checks_when_message_is_bumped_or_saved
656
- Logster.config.allow_grouping = true
657
- Logster.config.application_version = 'abc'
658
-
659
- @store.ignore = [/^ActiveRecord::RecordNotFound/]
660
- rate_limit = @store.register_rate_limit_per_minute(Logger::WARN, 0)
661
-
662
- message = @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
663
- assert_equal(1, rate_limit.retrieve_rate)
664
-
665
- @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
666
- assert_equal(2, rate_limit.retrieve_rate)
667
-
668
- @store.solve(message.key)
669
- @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
670
- assert_equal(2, rate_limit.retrieve_rate)
671
-
672
- @store.report(Logger::WARN, 'message 2', "Error!")
673
- assert_equal(3, rate_limit.retrieve_rate)
674
-
675
- @store.report(Logger::WARN, 'message 3', "ActiveRecord::RecordNotFound")
676
- assert_equal(3, rate_limit.retrieve_rate)
677
- ensure
678
- Logster.config.allow_grouping = false
679
- Logster.config.application_version = nil
680
- reset_redis
681
- end
682
-
683
- def test_rate_limits_with_prefix
684
- begin
685
- time = Time.now
686
- Timecop.freeze(time)
687
- current_namespace = 'first'
688
- @store.redis_prefix = Proc.new { current_namespace }
689
-
690
- called_first = 0
691
- called_second = 0
692
-
693
- @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_first += 1 }
694
- @store.report(Logger::WARN, "test", "test")
695
- assert_equal(1, called_first)
696
-
697
- current_namespace = 'second'
698
- @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_second += 1 }
699
- @store.report(Logger::WARN, "test", "test")
700
- assert_equal(1, called_first)
701
- assert_equal(1, called_second)
702
-
703
- Timecop.freeze(time + 10) do
704
- current_namespace = 'first'
705
- @store.report(Logger::WARN, "test", "test")
706
-
707
- assert_equal(2, called_first)
708
- assert_equal(1, called_second)
709
- end
710
- ensure
711
- reset_redis
712
- end
713
- end
714
-
715
- private
716
-
717
- def reset_redis
718
- @store.redis.flushall
719
- end
720
- 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
+ env = { test_env: "this is env" }
18
+ msg = @store.report(Logger::WARN, "test", "testing", env: env)
19
+ @store.delete(msg)
20
+ latest = @store.latest
21
+
22
+ assert_equal(0, latest.length)
23
+ assert_nil(@store.get_env(msg.key))
24
+ end
25
+
26
+ def test_latest
27
+ @store.report(Logger::WARN, "test", "IGNORE")
28
+ @store.report(Logger::WARN, "test", "This is a warning")
29
+ @store.report(Logger::WARN, "test", "This is another warning")
30
+
31
+ latest = @store.latest(limit: 2)
32
+
33
+ assert_equal(2, latest.length)
34
+ assert_equal("This is a warning", latest[0].message)
35
+ assert_equal("This is another warning", latest[1].message)
36
+ assert_equal(Logger::WARN, latest[1].severity)
37
+ assert_equal("test", latest[1].progname)
38
+ assert(!latest[1].key.nil?)
39
+ end
40
+
41
+ def test_latest_after
42
+ 10.times do |i|
43
+ @store.report(Logger::WARN, "test", "A#{i}")
44
+ end
45
+
46
+ message = @store.latest[-1]
47
+
48
+ 3.times do |i|
49
+ @store.report(Logger::WARN, "test", i.to_s)
50
+ end
51
+
52
+ message = @store.latest(after: message.key, limit: 3)[0]
53
+
54
+ assert_equal("0", message.message)
55
+ end
56
+
57
+ def test_latest_before
58
+ 10.times do
59
+ @store.report(Logger::WARN, "test", "A")
60
+ end
61
+ 10.times do
62
+ @store.report(Logger::WARN, "test", "B")
63
+ end
64
+ 10.times do
65
+ @store.report(Logger::WARN, "test", "C")
66
+ end
67
+
68
+ messages = @store.latest(limit: 10)
69
+ assert_equal("C", messages[0].message)
70
+ assert_equal(10, messages.length)
71
+
72
+ messages = @store.latest(limit: 10, before: messages[0].key)
73
+ assert_equal("B", messages[0].message)
74
+ assert_equal(10, messages.length)
75
+
76
+ messages = @store.latest(limit: 10, before: messages[0].key)
77
+ assert_equal("A", messages[0].message)
78
+ assert_equal(10, messages.length)
79
+
80
+ end
81
+
82
+ def test_get
83
+ a_env = { "a_message" => "A MESSAGE" }
84
+ a_message = @store.report(Logger::WARN, "test", "A", env: a_env)
85
+ b_message = @store.report(Logger::WARN, "test", "B")
86
+ @store.report(Logger::WARN, "test", "C")
87
+
88
+ a_message = @store.get(a_message.key)
89
+ assert_equal("A", a_message.message)
90
+ assert_equal("B", b_message.message)
91
+ assert(a_env <= a_message.env)
92
+
93
+ a_message = @store.get(a_message.key, load_env: false)
94
+ assert_equal("A", a_message.message)
95
+ assert_nil(a_message.env)
96
+ end
97
+
98
+ def test_save_saves_env_separately
99
+ env = { "myenv" => "thisisenv" }
100
+ message = @store.report(Logger::WARN, "test", "title", env: env)
101
+ message = @store.get(message.key, load_env: false)
102
+ assert_nil(message.env)
103
+
104
+ message = @store.get(message.key)
105
+ assert(env <= message.env)
106
+
107
+ assert(env <= @store.get_env(message.key))
108
+ end
109
+
110
+ def test_bulk_get
111
+ keys = []
112
+
113
+ 5.times do |n|
114
+ env = n == 0 ? nil : { "test_#{n}" => "envsss" }
115
+ keys << @store.report(Logger::WARN, "progname", "test_#{n}", env: env).key
116
+ end
117
+
118
+ messages = @store.bulk_get(keys)
119
+
120
+ 5.times do |n|
121
+ msg = messages[n]
122
+ assert_equal("test_#{n}", msg.message)
123
+ if n == 0
124
+ assert_equal(Logster::Message.default_env, msg.env)
125
+ else
126
+ assert({ "test_#{n}" => "envsss" } <= msg.env)
127
+ end
128
+ end
129
+ end
130
+
131
+ def test_get_env
132
+ env = { "my_little_env" => "some value" }
133
+ message = @store.report(Logger::WARN, "test", "A", env: env)
134
+ assert(env <= @store.get_env(message.key))
135
+ assert_nil(@store.get_env("nonexistentkey"))
136
+ end
137
+
138
+ def test_replace_and_bump
139
+ old_env = { "old_env" => "old value" }
140
+ message = @store.report(Logger::WARN, "test", "A", env: old_env)
141
+
142
+ unsaved_env = { "unsaved_env" => "lost value" }
143
+ message.env = unsaved_env
144
+
145
+ @store.replace_and_bump(message, save_env: false)
146
+
147
+ message = @store.get(message.key)
148
+ assert(old_env <= message.env)
149
+ refute(unsaved_env <= message.env)
150
+
151
+ saved_env = { "saved_env" => "saved value!" }
152
+ message.env = saved_env
153
+
154
+ @store.replace_and_bump(message)
155
+
156
+ message = @store.get(message.key)
157
+ assert(saved_env == message.env)
158
+ end
159
+
160
+ def test_backward_compatibility_no_loss_of_data
161
+ # previously we were storing env samples as a part of the main message json
162
+ # now we've switched to storing samples separately from the main message
163
+ # we need to make we don't lose env data of messages stored the old way
164
+ # when we migrate to the new system
165
+
166
+ # it probably makes sense to remove this test after a while (say 6-12 months)
167
+
168
+ Logster.config.allow_grouping = true
169
+ backtrace = "fake backtrace"
170
+ env = { "some_env" => "some env" }
171
+ message = Logster::Message.new(Logger::WARN, "", "title", count: 60)
172
+ message.env = env
173
+ message.backtrace = backtrace
174
+
175
+ @store.save(message)
176
+
177
+ # hack to force env to be stored with the main message json
178
+ @store.redis.hset(@store.send("hash_key"), message.key, message.to_json(exclude_env: false))
179
+
180
+ another_env = { "another_env" => "more env" }
181
+ message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: another_env)
182
+ message = @store.get(message.key)
183
+
184
+ assert(env <= message.env)
185
+ assert_equal(61, message.count)
186
+ # another_env is not merged cause count is 60, only the count is updated
187
+
188
+ # make sure we are now storing env samples separately
189
+ message = @store.get(message.key, load_env: false)
190
+ assert_nil(message.env)
191
+ ensure
192
+ Logster.config.allow_grouping = false
193
+ end
194
+
195
+ def test_backward_compatibility_no_loss_of_data_2
196
+ # same story as the test above, just a bit different
197
+
198
+ Logster.config.allow_grouping = true
199
+ backtrace = "fake backtrace"
200
+ env = { "some_env" => "some env" }
201
+ message = Logster::Message.new(Logger::WARN, "", "title")
202
+ message.env = env
203
+ message.backtrace = backtrace
204
+
205
+ @store.save(message)
206
+
207
+ # hack to force env to be stored with the main message json
208
+ @store.redis.hset(@store.send("hash_key"), message.key, message.to_json(exclude_env: false))
209
+
210
+ another_env = { "another_env" => "more env" }
211
+ message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: another_env)
212
+ message = @store.get(message.key)
213
+
214
+ assert_instance_of(Array, message.env)
215
+ assert(env <= message.env[0])
216
+ assert(another_env <= message.env[1])
217
+ assert_equal(2, message.env.size)
218
+ assert_equal(2, message.count)
219
+
220
+ # make sure we are now storing env samples separately
221
+ message = @store.get(message.key, load_env: false)
222
+ assert_nil(message.env)
223
+ ensure
224
+ Logster.config.allow_grouping = false
225
+ end
226
+
227
+ def test_merging_performance
228
+ Logster.config.allow_grouping = true
229
+ backtrace = "fake backtrace"
230
+ env = { "some_env" => "some env" }
231
+ another_env = { "another_env" => "more env" }
232
+ yet_another_env = { "moaar_env" => "more env" }
233
+
234
+ @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: env, count: 49)
235
+
236
+ message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: another_env)
237
+ assert_instance_of(Array, message.env)
238
+ assert_equal(2, message.env.size)
239
+ assert(env <= message.env[0])
240
+ assert(another_env <= message.env[1])
241
+
242
+ message = @store.report(Logger::WARN, "", "title", backtrace: backtrace, env: yet_another_env)
243
+ # we don't need to load env from redis cause we don't
244
+ # need to merge new env samples if count is 50 or more
245
+ assert_nil(message.env)
246
+ ensure
247
+ Logster.config.allow_grouping = false
248
+ end
249
+
250
+ def test_backlog
251
+ env = { "backlog_test" => "BACKLOG" }
252
+ @store.max_backlog = 1
253
+ deleted_msg = @store.report(Logger::WARN, "test", "A")
254
+ @store.report(Logger::WARN, "test", "A")
255
+ @store.report(Logger::WARN, "test", "A")
256
+ @store.report(Logger::WARN, "test", "B", env: env)
257
+
258
+ latest = @store.latest
259
+
260
+ assert_equal(1, latest.length)
261
+ assert_equal("B", latest[0].message)
262
+ assert(env <= latest[0].env)
263
+ assert_nil(@store.get(deleted_msg.key))
264
+ assert_nil(@store.get_env(deleted_msg.key))
265
+ end
266
+
267
+ def test_save_unsave
268
+ @store.max_backlog = 3
269
+ @store.report(Logger::WARN, "test", "A")
270
+ b_message = @store.report(Logger::WARN, "test", "B")
271
+ @store.protect b_message.key
272
+ c_message = @store.report(Logger::WARN, "test", "C")
273
+ @store.protect c_message.key
274
+ @store.report(Logger::WARN, "test", "D")
275
+
276
+ latest = @store.latest
277
+
278
+ assert_equal(3, latest.length)
279
+ assert_equal("B", latest[0].message)
280
+ assert_equal("C", latest[1].message)
281
+ assert_equal(true, latest[1].protected)
282
+ assert_equal("D", latest[2].message)
283
+
284
+ # Saved messages still accessible by key
285
+ assert_equal("B", @store.get(b_message.key).message)
286
+ assert_equal(true, @store.get(b_message.key).protected)
287
+
288
+ # Unsave does not delete message if still recent
289
+ @store.unprotect c_message.key
290
+ assert_equal("C", @store.get(c_message.key).message)
291
+ assert_equal(false, @store.get(c_message.key).protected)
292
+ end
293
+
294
+ def test_clear
295
+ env = { "clear_env" => "cllleear" }
296
+ @store.max_backlog = 25
297
+ a_message = @store.report(Logger::WARN, "test", "A", timestamp: Time.now - (24 * 60 * 60), env: env)
298
+ @store.protect a_message.key
299
+ 20.times do
300
+ @store.report(Logger::WARN, "test", "B", env: env)
301
+ end
302
+ c_message = @store.report(Logger::WARN, "test", "C", timestamp: Time.now + (24 * 60 * 60), env: env)
303
+ @store.protect c_message.key
304
+ d_message = @store.report(Logger::WARN, "test", "D", env: env)
305
+ 10.times do
306
+ @store.report(Logger::WARN, "test", "E", env: env)
307
+ end
308
+
309
+ latest = @store.latest
310
+ assert_equal(25, latest.length)
311
+
312
+ @store.clear
313
+
314
+ # Protected messages are still accessible by their key
315
+ assert_equal("C", @store.get(c_message.key).message)
316
+ assert(env <= @store.get_env(c_message.key))
317
+ # Unprotected messages are gone
318
+ assert_nil(@store.get(d_message.key))
319
+ assert_nil(@store.get_env(d_message.key))
320
+
321
+ # The latest list is rebuilt with protected messages, earliest first
322
+ # Including messages that previously fell off (A)
323
+ latest = @store.latest
324
+ assert_equal(2, latest.length)
325
+ assert_equal("A", latest[0].message)
326
+ assert_equal("C", latest[1].message)
327
+ assert(env <= latest[0].env)
328
+ assert(env <= latest[1].env)
329
+ end
330
+
331
+ def test_hash_cleanup
332
+ @store.max_backlog = 2
333
+ a_message = @store.report(Logger::WARN, "test", "A")
334
+ @store.report(Logger::WARN, "test", "B")
335
+ @store.report(Logger::WARN, "test", "C")
336
+
337
+ assert_nil(@store.get(a_message.key))
338
+ end
339
+
340
+ def test_filter_latest
341
+ @store.report(Logger::INFO, "test", "A")
342
+ @store.report(Logger::WARN, "test", "B")
343
+
344
+ messages = @store.latest
345
+ assert_equal(2, messages.length)
346
+
347
+ messages = @store.latest(after: messages.last.key)
348
+ assert_equal(0, messages.length)
349
+
350
+ 10.times do
351
+ @store.report(Logger::INFO, "test", "A")
352
+ end
353
+ @store.report(Logger::ERROR, "test", "C")
354
+ 10.times do
355
+ @store.report(Logger::INFO, "test", "A")
356
+ end
357
+
358
+ latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2)
359
+
360
+ assert_equal(2, latest.length)
361
+ assert_equal("B", latest[0].message)
362
+ assert_equal("C", latest[1].message)
363
+
364
+ @store.report(Logger::ERROR, "test", "E")
365
+ # respects after
366
+ latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2, after: latest[1].key)
367
+ assert_equal(1, latest.length)
368
+ end
369
+
370
+ def test_search
371
+ @store.report(Logger::INFO, "test", "ABCDEFG")
372
+ @store.report(Logger::INFO, "test", "TUVWXYZ")
373
+
374
+ messages = @store.latest
375
+ assert_equal(2, messages.length)
376
+
377
+ latest = @store.latest(search: "TUVWXYZ")
378
+
379
+ assert_equal(1, latest.length)
380
+ assert_equal("TUVWXYZ", latest[0].message)
381
+ end
382
+
383
+ def test_search_exclude_results
384
+ @store.report(Logger::INFO, "test", "ABCDEFG")
385
+ @store.report(Logger::INFO, "test", "TUVWXYZ")
386
+
387
+ messages = @store.latest
388
+ assert_equal(2, messages.length)
389
+
390
+ latest = @store.latest(search: "-ABCD")
391
+
392
+ assert_equal(1, latest.length)
393
+ assert_equal("TUVWXYZ", latest[0].message)
394
+ end
395
+
396
+ def test_regex_search
397
+ @store.report(Logger::INFO, "test", "pattern_1")
398
+ @store.report(Logger::INFO, "test", "pattern_2")
399
+
400
+ messages = @store.latest
401
+ assert_equal(2, messages.length)
402
+
403
+ latest = @store.latest(search: /^pattern_[1]$/)
404
+
405
+ assert_equal(1, latest.length)
406
+ end
407
+
408
+ def test_env_search
409
+ @store.report(Logger::INFO, "test", "message ABCD", env: { cluster: "business5" })
410
+ @store.report(Logger::INFO, "test", "message WXYZ", env: { cluster: "business7" })
411
+
412
+ messages = @store.latest
413
+ assert_equal(2, messages.length)
414
+
415
+ latest = @store.latest(search: "business5")
416
+
417
+ assert_equal(1, latest.length)
418
+ assert_equal("message ABCD", latest[0].message)
419
+
420
+ latest = @store.latest(search: "-business5")
421
+
422
+ assert_equal(1, latest.length)
423
+ assert_equal("message WXYZ", latest[0].message)
424
+
425
+ latest = @store.latest(search: /business/)
426
+
427
+ assert_equal(2, latest.length)
428
+ assert_equal(["message ABCD", "message WXYZ"], latest.map(&:message).sort)
429
+ end
430
+
431
+ def test_array_env_search_preserve_env
432
+ m1_original_env = [{ cluster: "business5" }, { cluster: "standard3" }]
433
+ m2_original_env = [{ cluster: "business2" }, { cluster: "standard7" }]
434
+
435
+ @store.report(Logger::INFO, "test", "message ABCD", env: m1_original_env, count: 2)
436
+ @store.report(Logger::INFO, "test", "message WXYZ", env: m2_original_env, count: 2)
437
+
438
+ messages = @store.latest
439
+ assert_equal(2, messages.length)
440
+
441
+ m1_key = messages[0].key
442
+ m2_key = messages[1].key
443
+
444
+ messages = @store.latest(search: "business")
445
+ assert_equal(2, messages.size)
446
+
447
+ # any hashes that don't match should be stripped from the env
448
+ # array but only temporarily until it's sent to the client
449
+ # env array should remain untouched in redis memory
450
+ assert_equal(["business5"], messages[0].env.map { |env| env["cluster"] })
451
+ assert_equal(1, messages[0].count)
452
+ assert_equal(["business2"], messages[1].env.map { |env| env["cluster"] })
453
+ assert_equal(1, messages[1].count)
454
+
455
+ m1 = @store.get(m1_key)
456
+ m2 = @store.get(m2_key)
457
+ # original env should preserved in redis memory
458
+ assert_equal(["business5", "standard3"], m1.env.map { |env| env["cluster"] })
459
+ assert_equal(["business2", "standard7"], m2.env.map { |env| env["cluster"] })
460
+ end
461
+
462
+ def test_both_env_and_title_match_search
463
+ @store.report(Logger::INFO, "test", "message", env: [{ cluster: "business15" }])
464
+ @store.report(Logger::INFO, "test", "message2", env: { cluster: "business15" })
465
+
466
+ messages = @store.latest
467
+ assert_equal(2, messages.size)
468
+
469
+ messages = @store.latest(search: "-business15")
470
+ assert_equal(0, messages.size)
471
+ end
472
+
473
+ def test_data_kept_intact_on_report_when_env_matches_an_ignore_pattern
474
+ begin
475
+ Logster.config.allow_grouping = true
476
+ backtrace = caller
477
+ message = @store.report(Logger::WARN, "", "my error", env: { whatever: "something", backtrace: backtrace })
478
+
479
+ @store.ignore = [
480
+ Logster::IgnorePattern.new("business")
481
+ ]
482
+ @store.report(Logger::WARN, "", "my error", env: { cluster: "business17", backtrace: backtrace })
483
+
484
+ message = @store.get(message.key)
485
+ assert(Array === message.env)
486
+ assert_equal(2, message.env.size)
487
+ # message2 shouldn't vanish even if
488
+ # its env matches an ignore pattern
489
+ # however it should be merged with message1
490
+ assert_equal("business17", message.env[1]["cluster"])
491
+ ensure
492
+ # reset so it doesn't affect other tests
493
+ @store.ignore = nil
494
+ Logster.config.allow_grouping = false
495
+ end
496
+ end
497
+
498
+ def test_array_env_negative_search
499
+ @store.report(Logger::INFO, "test", "message ABCD", env: [{ cluster: "business5" }, { cluster: "standard3" }], count: 2)
500
+ @store.report(Logger::INFO, "test", "message WXYZ", env: [{ cluster: "business2" }, { cluster: "standard7" }], count: 2)
501
+
502
+ messages = @store.latest
503
+ assert_equal(2, messages.length)
504
+
505
+ messages = @store.latest(search: "-business")
506
+ assert_equal(2, messages.size)
507
+
508
+ assert_equal(["standard3"], messages[0].env.map { |env| env["cluster"] })
509
+ assert_equal(1, messages[0].count)
510
+ assert_equal(["standard7"], messages[1].env.map { |env| env["cluster"] })
511
+ assert_equal(1, messages[1].count)
512
+ end
513
+
514
+ def test_negative_search_MUST_not_match_title_in_order_to_include_message
515
+ @store.report(Logger::INFO, "test", "message ABCD", env: [{ cluster: "business5" }, { cluster: "standard3" }], count: 2)
516
+
517
+ messages = @store.latest(search: "-ABCD")
518
+ assert_equal(0, messages.size) # cause title has ABCD
519
+ end
520
+
521
+ def test_positive_search_looks_at_title_OR_env
522
+ @store.report(Logger::INFO, "test", "message", env: [{ cluster: "business5 ABCDEFG" }, { cluster: "standard3" }], count: 2)
523
+
524
+ messages = @store.latest(search: "ABCDEFG")
525
+ assert_equal(1, messages.size)
526
+ assert_equal(1, messages[0].env.size)
527
+ assert_equal("business5 ABCDEFG", messages[0].env[0]["cluster"])
528
+ end
529
+
530
+ def test_backtrace
531
+ @store.report(Logger::INFO, "test", "pattern_1")
532
+ message = @store.latest(limit: 1).first
533
+ assert_match("test_backtrace", message.backtrace)
534
+ end
535
+
536
+ def test_ignore
537
+ @store.ignore = [/^test/]
538
+ @store.report(Logger::INFO, "test", "test it")
539
+ @store.report(Logger::INFO, "test", " test it")
540
+
541
+ assert_equal(1, @store.latest.count)
542
+ end
543
+
544
+ def test_solve
545
+ Logster.config.application_version = "abc"
546
+
547
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
548
+ m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1")
549
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace2")
550
+
551
+ assert_equal(3, @store.latest.count)
552
+
553
+ @store.solve(m.key)
554
+
555
+ assert_equal(1, @store.latest.count)
556
+
557
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
558
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
559
+
560
+ assert_equal(2, @store.latest.count)
561
+
562
+ ensure
563
+ Logster.config.application_version = nil
564
+ end
565
+
566
+ def test_solve_grouped
567
+ Logster.config.allow_grouping = true
568
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
569
+ m = @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg" })
570
+
571
+ assert_equal(1, @store.latest.count)
572
+
573
+ @store.solve(m.key)
574
+
575
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
576
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg" })
577
+
578
+ assert_equal(0, @store.latest.count)
579
+
580
+ ensure
581
+ Logster.config.allow_grouping = false
582
+ end
583
+
584
+ def test_clears_solved
585
+ m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1", env: { "application_version" => "abc" })
586
+ @store.solve(m.key)
587
+
588
+ assert_equal(1, @store.solved.length)
589
+
590
+ @store.clear
591
+ assert_equal(0, @store.solved.length)
592
+ end
593
+
594
+ def test_solving_with_some_missing_version
595
+
596
+ m = @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz" })
597
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
598
+
599
+ @store.solve(m.key)
600
+
601
+ assert_equal(1, @store.latest.count)
602
+ end
603
+
604
+ def test_env
605
+ env = Rack::MockRequest.env_for("/test").merge(
606
+ "HTTP_HOST" => "www.site.com",
607
+ "HTTP_USER_AGENT" => "SOME WHERE"
608
+ )
609
+ orig = env.dup
610
+ orig["test"] = "tests"
611
+ orig["test1"] = "tests1"
612
+ Logster.add_to_env(env, "test", "tests")
613
+ Logster.add_to_env(env, "test1", "tests1")
614
+
615
+ orig.delete_if do |k, v|
616
+ !%w{
617
+ HTTP_HOST
618
+ REQUEST_METHOD
619
+ HTTP_USER_AGENT
620
+ test
621
+ test1
622
+ }.include? k
623
+ end
624
+
625
+ @store.report(Logger::INFO, "test", "test", env: env)
626
+
627
+ env = @store.latest.last.env
628
+
629
+ env.delete "hostname"
630
+ env.delete "process_id"
631
+
632
+ assert_equal(orig, env)
633
+ end
634
+
635
+ def test_rate_limits
636
+ %w{minute hour}.each do |duration|
637
+ begin
638
+ called = false
639
+
640
+ assert_instance_of(
641
+ Logster::RedisRateLimiter,
642
+ @store.public_send("register_rate_limit_per_#{duration}", Logger::WARN, 0) do
643
+ called = true
644
+ end
645
+ )
646
+
647
+ @store.report(Logger::WARN, "test", "test")
648
+ assert called
649
+ ensure
650
+ reset_redis
651
+ end
652
+ end
653
+ end
654
+
655
+ def test_rate_limits_only_checks_when_message_is_bumped_or_saved
656
+ Logster.config.allow_grouping = true
657
+ Logster.config.application_version = 'abc'
658
+
659
+ @store.ignore = [/^ActiveRecord::RecordNotFound/]
660
+ rate_limit = @store.register_rate_limit_per_minute(Logger::WARN, 0)
661
+
662
+ message = @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
663
+ assert_equal(1, rate_limit.retrieve_rate)
664
+
665
+ @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
666
+ assert_equal(2, rate_limit.retrieve_rate)
667
+
668
+ @store.solve(message.key)
669
+ @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
670
+ assert_equal(2, rate_limit.retrieve_rate)
671
+
672
+ @store.report(Logger::WARN, 'message 2', "Error!")
673
+ assert_equal(3, rate_limit.retrieve_rate)
674
+
675
+ @store.report(Logger::WARN, 'message 3', "ActiveRecord::RecordNotFound")
676
+ assert_equal(3, rate_limit.retrieve_rate)
677
+ ensure
678
+ Logster.config.allow_grouping = false
679
+ Logster.config.application_version = nil
680
+ reset_redis
681
+ end
682
+
683
+ def test_rate_limits_with_prefix
684
+ begin
685
+ time = Time.now
686
+ Timecop.freeze(time)
687
+ current_namespace = 'first'
688
+ @store.redis_prefix = Proc.new { current_namespace }
689
+
690
+ called_first = 0
691
+ called_second = 0
692
+
693
+ @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_first += 1 }
694
+ @store.report(Logger::WARN, "test", "test")
695
+ assert_equal(1, called_first)
696
+
697
+ current_namespace = 'second'
698
+ @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_second += 1 }
699
+ @store.report(Logger::WARN, "test", "test")
700
+ assert_equal(1, called_first)
701
+ assert_equal(1, called_second)
702
+
703
+ Timecop.freeze(time + 10) do
704
+ current_namespace = 'first'
705
+ @store.report(Logger::WARN, "test", "test")
706
+
707
+ assert_equal(2, called_first)
708
+ assert_equal(1, called_second)
709
+ end
710
+ ensure
711
+ reset_redis
712
+ end
713
+ end
714
+
715
+ private
716
+
717
+ def reset_redis
718
+ @store.redis.flushall
719
+ end
720
+ end