logster 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/lib/logster/configuration.rb +1 -1
- data/lib/logster/redis_store.rb +42 -24
- data/lib/logster/version.rb +1 -1
- data/test/logster/test_redis_rate_limiter.rb +13 -8
- data/test/logster/test_redis_store.rb +52 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97266a5b02f788e8537daaf28e0adff771a3bf6b
|
4
|
+
data.tar.gz: f43b618a847f9e98f54ecb9e469a29e0354a6947
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22657415d4f4173ed2a1850e43a146d81db3c53e33d6009f99069fbe81671fb65d88809fc79a963b06744a17e288364706d1ef63127bcacb3076cc9be789e28d
|
7
|
+
data.tar.gz: 7e57a8c7862c3375948e2a097b89c1c1d3f023e33a32c928196371e3c7ef4c02b90df6828790d12fafb49c21acccf5b7810abe32220f4030e012ed463a271081
|
data/README.md
CHANGED
@@ -97,6 +97,10 @@ Logster UI is built using [Ember.js](http://emberjs.com/)
|
|
97
97
|
|
98
98
|
# CHANGELOG
|
99
99
|
|
100
|
+
- 2015-03-18: Version 1.2.0
|
101
|
+
- Fix: Move Redis configuration into RedisStore.
|
102
|
+
- Feature: Allow `RedisStore#redis_prefix` to either be a String or a Proc.
|
103
|
+
|
100
104
|
- 2015-02-11: Version 1.1.1
|
101
105
|
- Feature: Error rate can now be tracked in one minute and one hour buckets.
|
102
106
|
|
data/lib/logster/redis_store.rb
CHANGED
@@ -4,20 +4,15 @@ require 'logster/base_store'
|
|
4
4
|
module Logster
|
5
5
|
class RedisRateLimiter
|
6
6
|
BUCKETS = 6
|
7
|
+
PREFIX = "__LOGSTER__RATE_LIMIT".freeze
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(redis, severities, limit, duration, callback = nil)
|
11
|
-
@redis = use_raw_connection? ? Logster.config.redis_raw_connection : redis
|
9
|
+
def initialize(redis, severities, limit, duration, redis_prefix = nil, callback = nil)
|
12
10
|
@severities = severities
|
13
11
|
@limit = limit
|
14
12
|
@duration = duration
|
15
13
|
@callback = callback
|
16
|
-
|
17
|
-
|
18
|
-
# Triggers callback when log levels of :debug, :info and :warn occurs 20 times within 30 secs
|
19
|
-
@key = "#{key_prefix}#{@severities.join("")}:#{@limit}:#{@duration}"
|
20
|
-
@callback_key = "#{@key}:callback_triggered"
|
14
|
+
@redis_prefix = redis_prefix
|
15
|
+
@redis = redis
|
21
16
|
@bucket_range = @duration / BUCKETS
|
22
17
|
end
|
23
18
|
|
@@ -25,7 +20,7 @@ module Logster
|
|
25
20
|
return unless @severities.include?(severity)
|
26
21
|
time = Time.now.to_i
|
27
22
|
num = bucket_number(time)
|
28
|
-
redis_key = "#{
|
23
|
+
redis_key = "#{key}:#{num}"
|
29
24
|
|
30
25
|
current_rate = @redis.eval <<-LUA
|
31
26
|
local bucket_number = #{num}
|
@@ -48,20 +43,32 @@ module Logster
|
|
48
43
|
return (retrieve_rate() + bucket_count)
|
49
44
|
LUA
|
50
45
|
|
51
|
-
if !@redis.get(
|
46
|
+
if !@redis.get(callback_key) && (current_rate >= @limit)
|
52
47
|
@callback.call(current_rate) if @callback
|
53
|
-
@redis.set(
|
48
|
+
@redis.set(callback_key, 1)
|
54
49
|
end
|
55
50
|
|
56
51
|
current_rate
|
57
52
|
end
|
58
53
|
|
54
|
+
def key
|
55
|
+
# "_LOGSTER_RATE_LIMIT:012:20:30"
|
56
|
+
# Triggers callback when log levels of :debug, :info and :warn occurs 20 times within 30 secs
|
57
|
+
"#{key_prefix}:#{@severities.join("")}:#{@limit}:#{@duration}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def callback_key
|
61
|
+
"#{@key}:callback_triggered"
|
62
|
+
end
|
63
|
+
|
59
64
|
private
|
60
65
|
|
61
66
|
def key_prefix
|
62
|
-
|
63
|
-
|
64
|
-
|
67
|
+
if @redis_prefix
|
68
|
+
"#{@redis_prefix.call}:#{PREFIX}"
|
69
|
+
else
|
70
|
+
PREFIX
|
71
|
+
end
|
65
72
|
end
|
66
73
|
|
67
74
|
def mget_keys(bucket_num)
|
@@ -78,15 +85,12 @@ module Logster
|
|
78
85
|
def bucket_expiry(time)
|
79
86
|
@duration - ((time % @duration) % @bucket_range)
|
80
87
|
end
|
81
|
-
|
82
|
-
def use_raw_connection?
|
83
|
-
Logster.config.redis_prefix && Logster.config.redis_raw_connection
|
84
|
-
end
|
85
88
|
end
|
86
89
|
|
87
90
|
class RedisStore < BaseStore
|
88
91
|
|
89
|
-
attr_accessor :redis, :max_backlog
|
92
|
+
attr_accessor :redis, :max_backlog, :redis_raw_connection
|
93
|
+
attr_writer :redis_prefix
|
90
94
|
|
91
95
|
def initialize(redis = nil)
|
92
96
|
super()
|
@@ -269,10 +273,16 @@ module Logster
|
|
269
273
|
register_rate_limit(severities, limit, 3600, block)
|
270
274
|
end
|
271
275
|
|
276
|
+
def redis_prefix
|
277
|
+
return 'default'.freeze if !@redis_prefix
|
278
|
+
@prefix_is_proc ||= @redis_prefix.respond_to?(:call)
|
279
|
+
@prefix_is_proc ? @redis_prefix.call : @redis_prefix
|
280
|
+
end
|
281
|
+
|
272
282
|
protected
|
273
283
|
|
274
284
|
def rate_limits
|
275
|
-
@rate_limits ||=
|
285
|
+
@rate_limits ||= {}
|
276
286
|
end
|
277
287
|
|
278
288
|
def clear_solved(count = nil)
|
@@ -395,7 +405,9 @@ module Logster
|
|
395
405
|
end
|
396
406
|
|
397
407
|
def check_rate_limits(severity)
|
398
|
-
|
408
|
+
rate_limits_to_check = rate_limits[self.redis_prefix]
|
409
|
+
return if !rate_limits_to_check
|
410
|
+
rate_limits_to_check.each { |rate_limit| rate_limit.check(severity) }
|
399
411
|
end
|
400
412
|
|
401
413
|
def solved_key
|
@@ -422,8 +434,14 @@ module Logster
|
|
422
434
|
|
423
435
|
def register_rate_limit(severities, limit, duration, callback)
|
424
436
|
severities = [severities] unless severities.is_a?(Array)
|
425
|
-
|
426
|
-
|
437
|
+
redis = (@redis_raw_connection && @redis_prefix) ? @redis_raw_connection : @redis
|
438
|
+
|
439
|
+
rate_limiter = RedisRateLimiter.new(
|
440
|
+
redis, severities, limit, duration, Proc.new { redis_prefix }, callback
|
441
|
+
)
|
442
|
+
|
443
|
+
rate_limits[self.redis_prefix] ||= []
|
444
|
+
rate_limits[self.redis_prefix] << rate_limiter
|
427
445
|
rate_limiter
|
428
446
|
end
|
429
447
|
end
|
data/lib/logster/version.rb
CHANGED
@@ -17,7 +17,7 @@ class TestRedisRateLimiter < Minitest::Test
|
|
17
17
|
called = 0
|
18
18
|
|
19
19
|
@rate_limiter = Logster::RedisRateLimiter.new(
|
20
|
-
@redis, [Logger::WARN], 8, 60, Proc.new { called += 1 }
|
20
|
+
@redis, [Logger::WARN], 8, 60, nil, Proc.new { called += 1 }
|
21
21
|
)
|
22
22
|
|
23
23
|
assert_equal(1, @rate_limiter.check(Logger::WARN))
|
@@ -82,7 +82,7 @@ class TestRedisRateLimiter < Minitest::Test
|
|
82
82
|
called = 0
|
83
83
|
|
84
84
|
@rate_limiter = Logster::RedisRateLimiter.new(
|
85
|
-
@redis, [Logger::WARN, Logger::ERROR], 4, 60, Proc.new { called += 1 }
|
85
|
+
@redis, [Logger::WARN, Logger::ERROR], 4, 60, nil, Proc.new { called += 1 }
|
86
86
|
)
|
87
87
|
|
88
88
|
assert_equal(1, @rate_limiter.check(Logger::WARN))
|
@@ -138,14 +138,19 @@ class TestRedisRateLimiter < Minitest::Test
|
|
138
138
|
def test_raw_connection
|
139
139
|
time = Time.new(2015, 1, 1, 1, 1)
|
140
140
|
Timecop.freeze(time)
|
141
|
-
Logster.
|
142
|
-
Logster.config.redis_raw_connection = @redis
|
143
|
-
|
144
|
-
@rate_limiter = Logster::RedisRateLimiter.new(nil, [Logger::WARN], 1, 60)
|
141
|
+
@rate_limiter = Logster::RedisRateLimiter.new(@redis, [Logger::WARN], 1, 60, Proc.new { "lobster" })
|
145
142
|
|
146
143
|
assert_equal(1, @rate_limiter.check(Logger::WARN))
|
147
|
-
assert_includes(key, "lobster")
|
148
144
|
assert_redis_key(60, 0)
|
145
|
+
|
146
|
+
array = ['lobster1', 'lobster2']
|
147
|
+
|
148
|
+
@rate_limiter = Logster::RedisRateLimiter.new(
|
149
|
+
@redis, [Logger::WARN], 1, 60, Proc.new { array.delete_at(0) }
|
150
|
+
)
|
151
|
+
|
152
|
+
assert_includes(key, "lobster1")
|
153
|
+
assert_includes(key, "lobster2")
|
149
154
|
end
|
150
155
|
|
151
156
|
private
|
@@ -172,7 +177,7 @@ class TestRedisRateLimiter < Minitest::Test
|
|
172
177
|
|
173
178
|
def assert_redis_key(expected_ttl, expected_bucket_number)
|
174
179
|
redis_key = "#{key}:#{expected_bucket_number}"
|
175
|
-
assert(@redis.get(redis_key))
|
180
|
+
assert(@redis.get(redis_key), "the right bucket should be created")
|
176
181
|
assert_equal(expected_ttl, @redis.ttl(redis_key))
|
177
182
|
end
|
178
183
|
end
|
@@ -326,19 +326,61 @@ class TestRedisStore < Minitest::Test
|
|
326
326
|
assert_equal(orig, env)
|
327
327
|
end
|
328
328
|
|
329
|
-
|
330
|
-
|
331
|
-
|
329
|
+
def test_rate_limits
|
330
|
+
%w{minute hour}.each do |duration|
|
331
|
+
begin
|
332
|
+
called = false
|
333
|
+
|
334
|
+
assert_instance_of(
|
335
|
+
Logster::RedisRateLimiter,
|
336
|
+
@store.public_send("register_rate_limit_per_#{duration}", Logger::WARN, 0) do
|
337
|
+
called = true
|
338
|
+
end
|
339
|
+
)
|
340
|
+
|
341
|
+
@store.report(Logger::WARN, "test", "test")
|
342
|
+
assert called
|
343
|
+
ensure
|
344
|
+
reset_redis
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def test_rate_limits_with_prefix
|
350
|
+
begin
|
351
|
+
time = Time.now
|
352
|
+
Timecop.freeze(time)
|
353
|
+
current_namespace = 'first'
|
354
|
+
@store.redis_prefix = Proc.new { current_namespace }
|
355
|
+
|
356
|
+
called_first = 0
|
357
|
+
called_second = 0
|
332
358
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
called = true
|
337
|
-
end
|
338
|
-
)
|
359
|
+
@store.register_rate_limit_per_minute(Logger::WARN, 0) { called_first += 1 }
|
360
|
+
@store.report(Logger::WARN, "test", "test")
|
361
|
+
assert_equal(1, called_first)
|
339
362
|
|
363
|
+
current_namespace = 'second'
|
364
|
+
@store.register_rate_limit_per_minute(Logger::WARN, 0) { called_second += 1 }
|
340
365
|
@store.report(Logger::WARN, "test", "test")
|
341
|
-
|
366
|
+
assert_equal(1, called_first)
|
367
|
+
assert_equal(1, called_second)
|
368
|
+
|
369
|
+
Timecop.freeze(time + 10) do
|
370
|
+
current_namespace = 'first'
|
371
|
+
@store.report(Logger::WARN, "test", "test")
|
372
|
+
|
373
|
+
assert_equal(2, called_first)
|
374
|
+
assert_equal(1, called_second)
|
375
|
+
end
|
376
|
+
ensure
|
377
|
+
reset_redis
|
342
378
|
end
|
343
379
|
end
|
380
|
+
|
381
|
+
private
|
382
|
+
|
383
|
+
def reset_redis
|
384
|
+
@store.redis.flushall
|
385
|
+
end
|
344
386
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- UI for viewing logs in Rack
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|