logster 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +172 -169
  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 +106 -100
  22. data/assets/stylesheets/client-app.css +1 -1
  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 +0 -0
  33. data/client-app/app/components/actions-menu.js +43 -37
  34. data/client-app/app/components/env-tab.js +80 -44
  35. data/client-app/app/components/message-info.js +0 -0
  36. data/client-app/app/components/message-row.js +0 -0
  37. data/client-app/app/components/panel-resizer.js +0 -0
  38. data/client-app/app/components/tab-contents.js +27 -27
  39. data/client-app/app/components/tabbed-section.js +0 -0
  40. data/client-app/app/components/time-formatter.js +0 -0
  41. data/client-app/app/components/update-time.js +0 -0
  42. data/client-app/app/controllers/index.js +0 -0
  43. data/client-app/app/controllers/show.js +0 -0
  44. data/client-app/app/index.html +29 -29
  45. data/client-app/app/initializers/app-init.js +67 -72
  46. data/client-app/app/lib/preload.js +20 -14
  47. data/client-app/app/lib/utilities.js +149 -140
  48. data/client-app/app/models/message-collection.js +0 -0
  49. data/client-app/app/models/message.js +100 -100
  50. data/client-app/app/resolver.js +0 -0
  51. data/client-app/app/router.js +0 -0
  52. data/client-app/app/routes/index.js +0 -0
  53. data/client-app/app/routes/show.js +0 -0
  54. data/client-app/app/styles/app.css +527 -521
  55. data/client-app/app/templates/application.hbs +2 -2
  56. data/client-app/app/templates/components/actions-menu.hbs +12 -12
  57. data/client-app/app/templates/components/env-tab.hbs +10 -10
  58. data/client-app/app/templates/components/message-info.hbs +41 -41
  59. data/client-app/app/templates/components/message-row.hbs +15 -15
  60. data/client-app/app/templates/components/panel-resizer.hbs +3 -3
  61. data/client-app/app/templates/components/tabbed-section.hbs +10 -10
  62. data/client-app/app/templates/components/time-formatter.hbs +1 -1
  63. data/client-app/app/templates/index.hbs +58 -58
  64. data/client-app/app/templates/show.hbs +7 -7
  65. data/client-app/config/environment.js +51 -51
  66. data/client-app/config/optional-features.json +3 -3
  67. data/client-app/config/targets.js +18 -18
  68. data/client-app/ember-cli-build.js +29 -29
  69. data/client-app/package-lock.json +11365 -11365
  70. data/client-app/package.json +56 -56
  71. data/client-app/testem.js +25 -25
  72. data/client-app/tests/index.html +34 -34
  73. data/client-app/tests/integration/components/env-tab-test.js +123 -73
  74. data/client-app/tests/integration/components/message-info-test.js +111 -26
  75. data/client-app/tests/test-helper.js +8 -8
  76. data/client-app/tests/unit/controllers/index-test.js +12 -12
  77. data/client-app/tests/unit/controllers/show-test.js +12 -12
  78. data/client-app/tests/unit/initializers/app-init-test.js +31 -31
  79. data/client-app/tests/unit/routes/index-test.js +11 -11
  80. data/client-app/tests/unit/routes/show-test.js +11 -11
  81. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  82. data/lib/logster.rb +54 -54
  83. data/lib/logster/base_store.rb +141 -141
  84. data/lib/logster/configuration.rb +26 -25
  85. data/lib/logster/defer_logger.rb +14 -14
  86. data/lib/logster/ignore_pattern.rb +65 -65
  87. data/lib/logster/logger.rb +113 -113
  88. data/lib/logster/message.rb +212 -212
  89. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  90. data/lib/logster/middleware/reporter.rb +55 -55
  91. data/lib/logster/middleware/viewer.rb +222 -221
  92. data/lib/logster/rails/railtie.rb +63 -63
  93. data/lib/logster/redis_store.rb +566 -566
  94. data/lib/logster/scheduler.rb +54 -54
  95. data/lib/logster/version.rb +3 -3
  96. data/lib/logster/web.rb +14 -14
  97. data/logster.gemspec +35 -35
  98. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  99. data/test/fake_data/Gemfile +4 -4
  100. data/test/fake_data/generate.rb +10 -10
  101. data/test/logster/middleware/test_reporter.rb +19 -19
  102. data/test/logster/middleware/test_viewer.rb +96 -96
  103. data/test/logster/test_base_store.rb +147 -147
  104. data/test/logster/test_defer_logger.rb +34 -34
  105. data/test/logster/test_ignore_pattern.rb +41 -41
  106. data/test/logster/test_logger.rb +86 -86
  107. data/test/logster/test_message.rb +119 -119
  108. data/test/logster/test_redis_rate_limiter.rb +230 -230
  109. data/test/logster/test_redis_store.rb +720 -720
  110. data/test/test_helper.rb +38 -38
  111. data/vendor/assets/javascripts/logster.js.erb +39 -39
  112. metadata +1 -10
  113. data/client-app/app/components/tab-link.js +0 -5
  114. data/client-app/tests/integration/components/actions-menu-test.js +0 -26
  115. data/client-app/tests/integration/components/message-row-test.js +0 -26
  116. data/client-app/tests/integration/components/panel-resizer-test.js +0 -26
  117. data/client-app/tests/integration/components/tab-contents-test.js +0 -26
  118. data/client-app/tests/integration/components/tab-link-test.js +0 -26
  119. data/client-app/tests/integration/components/tabbed-section-test.js +0 -26
  120. data/client-app/tests/integration/components/time-formatter-test.js +0 -26
  121. data/client-app/tests/integration/components/update-time-test.js +0 -26
@@ -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