fluent-plugin-redis-counter 0.1.0 → 0.1.1

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.
@@ -1,3 +1,7 @@
1
+ ## 0.1.1
2
+ * add 'count_key_format' in order to make time-dependent redis keyname
3
+ * bug fix
4
+
1
5
  ## 0.1.0
2
6
  * design changed.
3
7
 
data/README.md CHANGED
@@ -28,6 +28,15 @@ fluent-plugin-redis-counter is hosted by [RubyGems.org](https://rubygems.org/).
28
28
  count_key foo-status2xx # key-name for Redis
29
29
  count_value 1 # count-up amount(default: 1, negative value is allowed)
30
30
  </pattern>
31
+
32
+ # time-dependent redis keyname
33
+ # for example, "foo-status2xx-%Y-%m-%d" will be formatted to "foo-status2xx-2012-06-21".
34
+ # rules for placeholder(%Y, etc.) is similar to out_file plugin.
35
+ <pattern>
36
+ match_status ^2[0-9][0-9]$
37
+ count_key_format foo-statux2xx-%d
38
+ localtime # time-zone(default: localtime, it can be "utc" or "localtime")
39
+ </pattern>
31
40
  </match>
32
41
 
33
42
  # Example
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -3,11 +3,11 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "fluent-plugin-redis-counter"
6
- s.version = "0.1.0"
6
+ s.version = "0.1.1"
7
7
  s.description = "fluent-plugin-redis-counter is a fluent plugin to count-up/down redis keys."
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Buntaro Okada"]
10
- s.date = %q{2012-06-19}
10
+ s.date = %q{2012-06-21}
11
11
  s.email = "kbinani.bt@gmail.com"
12
12
  s.homepage = "https://github.com/kbinani/fluent-plugin-redis-counter"
13
13
  s.summary = "Redis counter plugin for fluent"
@@ -15,32 +15,14 @@ module Fluent
15
15
  @port = conf.has_key?('port') ? conf['port'].to_i : 6379
16
16
  @db_number = conf.has_key?('db_number') ? conf['db_number'].to_i : nil
17
17
  @patterns = []
18
- conf.elements.select { |e|
19
- e.name == 'pattern'
20
- }.each { |e|
21
- if e.has_key?('count_key') == false
22
- raise Fluent::ConfigError, '"count_key" is required.'
23
- end
24
- count_value = 1
25
- if e.has_key?('count_value')
26
- begin
27
- count_value = Integer(e['count_value'])
28
- rescue
29
- raise Fluent::ConfigError, 'invalid "count_value", integer required.'
30
- end
18
+ conf.elements.select { |element|
19
+ element.name == 'pattern'
20
+ }.each { |element|
21
+ begin
22
+ @patterns << Pattern.new(element)
23
+ rescue RedisCounterException => e
24
+ raise Fluent::ConfigError, e.message
31
25
  end
32
- matches = {}
33
- e.each_key { |key|
34
- if key =~ /^match_/
35
- name = key['match_'.size .. key.size]
36
- matches[name] = Regexp.new(e[key])
37
- end
38
- }
39
- @patterns << {
40
- 'matches' => matches,
41
- 'count_key' => e['count_key'],
42
- 'count_value' => count_value
43
- }
44
26
  }
45
27
  end
46
28
 
@@ -57,7 +39,7 @@ module Fluent
57
39
  end
58
40
 
59
41
  def format(tag, time, record)
60
- record.to_msgpack
42
+ [tag, time, record].to_msgpack
61
43
  end
62
44
 
63
45
  def write(chunk)
@@ -65,43 +47,86 @@ module Fluent
65
47
  table.default = 0
66
48
  chunk.open { |io|
67
49
  begin
68
- MessagePack::Unpacker.new(io).each { |record|
69
- matched_pattern = get_matched_pattern(record)
70
- if matched_pattern != nil && matched_pattern['count_value'] != 0
71
- table[matched_pattern['count_key']] += matched_pattern['count_value']
72
- end
50
+ MessagePack::Unpacker.new(io).each { |message|
51
+ (tag, time, record) = message
52
+ @patterns.select { |pattern|
53
+ pattern.is_match?(record)
54
+ }.each{ |pattern|
55
+ table[pattern.get_count_key(time)] += pattern.count_value
56
+ }
73
57
  }
74
58
  rescue EOFError
75
59
  # EOFError always occured when reached end of chunk.
76
60
  end
77
61
  }
78
- table.each_key { |key|
79
- if (value = table[key]) != 0
80
- @redis.incrby(key, value)
81
- end
62
+ table.each_pair.select { |key, value|
63
+ value != 0
64
+ }.each { |key, value|
65
+ @redis.incrby(key, value)
82
66
  }
83
67
  end
84
68
 
85
- private
86
- def get_matched_pattern(record)
87
- @patterns.each { |pattern|
88
- all_matched = true
89
- pattern['matches'].each_key{ |key|
90
- if !record.has_key?(key)
91
- all_matched = false
92
- break
93
- else
94
- if !(record[key] =~ pattern['matches'][key])
95
- all_matched = false
96
- break
97
- end
69
+ class RedisCounterException < Exception
70
+ end
71
+
72
+ class Pattern
73
+ attr_reader :matches, :count_value
74
+
75
+ def initialize(conf_element)
76
+ if !conf_element.has_key?('count_key') && !conf_element.has_key?('count_key_format')
77
+ raise RedisCounterException, '"count_key" or "count_key_format" is required.'
78
+ end
79
+ if conf_element.has_key?('count_key') && conf_element.has_key?('count_key_format')
80
+ raise RedisCounterException, 'both "count_key" and "count_key_format" are specified.'
81
+ end
82
+
83
+ if conf_element.has_key?('count_key')
84
+ @count_key = conf_element['count_key']
85
+ else
86
+ if conf_element.has_key?('localtime') && conf_element.has_key?('utc')
87
+ raise RedisCounterException, 'both "localtime" and "utc" are specified.'
88
+ end
89
+ is_localtime = true
90
+ if conf_element.has_key?('utc')
91
+ is_localtime = false
92
+ end
93
+ @count_key_format = TimeFormatter.new(conf_element['count_key_format'], is_localtime)
94
+ end
95
+
96
+ @count_value = 1
97
+ if conf_element.has_key?('count_value')
98
+ begin
99
+ @count_value = Integer(conf_element['count_value'])
100
+ rescue
101
+ raise RedisCounterException, 'invalid "count_value", integer required.'
102
+ end
103
+ end
104
+
105
+ @matches = {}
106
+ conf_element.each_pair.select { |key, value|
107
+ key =~ /^match_/
108
+ }.each { |key, value|
109
+ name = key['match_'.size .. key.size]
110
+ @matches[name] = Regexp.new(value)
111
+ }
112
+ end
113
+
114
+ def is_match?(record)
115
+ @matches.each_pair{ |key, value|
116
+ if !record.has_key?(key) || !(record[key] =~ value)
117
+ return false
98
118
  end
99
119
  }
100
- if all_matched
101
- return pattern
120
+ return true
121
+ end
122
+
123
+ def get_count_key(time)
124
+ if @count_key_format == nil
125
+ @count_key
126
+ else
127
+ @count_key_format.format(time)
102
128
  end
103
- }
104
- return nil
129
+ end
105
130
  end
106
131
  end
107
132
  end
@@ -16,6 +16,7 @@ class RedisCounterTest < Test::Unit::TestCase
16
16
  )
17
17
  redis.del("a")
18
18
  redis.del("b")
19
+ redis.del("foo-2012-06-21")
19
20
  redis.quit
20
21
  end
21
22
 
@@ -50,15 +51,15 @@ class RedisCounterTest < Test::Unit::TestCase
50
51
  assert_equal 1, driver.instance.db_number
51
52
  assert_equal 2, driver.instance.patterns.size
52
53
 
53
- assert_equal 2, driver.instance.patterns[0]['matches'].size
54
- assert_equal Regexp.new('^2[0-9]{2}$'), driver.instance.patterns[0]['matches']['status']
55
- assert_equal Regexp.new('^https'), driver.instance.patterns[0]['matches']['url']
56
- assert_equal 'status-normal', driver.instance.patterns[0]['count_key']
57
- assert_equal 1, driver.instance.patterns[0]['count_value']
54
+ assert_equal 2, driver.instance.patterns[0].matches.size
55
+ assert_equal Regexp.new('^2[0-9]{2}$'), driver.instance.patterns[0].matches['status']
56
+ assert_equal Regexp.new('^https'), driver.instance.patterns[0].matches['url']
57
+ assert_equal 'status-normal', driver.instance.patterns[0].get_count_key(Time.now.to_i)
58
+ assert_equal 1, driver.instance.patterns[0].count_value
58
59
 
59
- assert_equal 0, driver.instance.patterns[1]['matches'].size
60
- assert_equal 'foo', driver.instance.patterns[1]['count_key']
61
- assert_equal 2, driver.instance.patterns[1]['count_value']
60
+ assert_equal 0, driver.instance.patterns[1].matches.size
61
+ assert_equal 'foo', driver.instance.patterns[1].get_count_key(Time.now.to_i)
62
+ assert_equal 2, driver.instance.patterns[1].count_value
62
63
  end
63
64
 
64
65
  def test_configure_count_key_required
@@ -70,7 +71,58 @@ class RedisCounterTest < Test::Unit::TestCase
70
71
  ]
71
72
  flunk
72
73
  rescue Fluent::ConfigError => e
73
- assert_equal '"count_key" is required.', e.message
74
+ assert_equal '"count_key" or "count_key_format" is required.', e.message
75
+ end
76
+ end
77
+
78
+ def test_configure_count_key_duplicated
79
+ begin
80
+ create_driver %[
81
+ <pattern>
82
+ count_key foo
83
+ count_key_format foo-%Y
84
+ </pattern>
85
+ ]
86
+ flunk
87
+ rescue Fluent::ConfigError => e
88
+ assert_equal 'both "count_key" and "count_key_format" are specified.', e.message
89
+ end
90
+ end
91
+
92
+ def test_configure_count_key_format_utc
93
+ driver = create_driver %[
94
+ <pattern>
95
+ count_key_format foo-%Y-%m-%d-%H-%M-%S
96
+ utc
97
+ </pattern>
98
+ ]
99
+ time = Time.parse('2011-06-21 03:12:01 UTC').to_i
100
+ assert_equal 'foo-2011-06-21-03-12-01', driver.instance.patterns[0].get_count_key(time)
101
+ end
102
+
103
+ def test_configure_count_key_format_localtime
104
+ driver = create_driver %[
105
+ <pattern>
106
+ count_key_format foo-%Y-%m-%d-%H-%M-%S
107
+ localtime
108
+ </pattern>
109
+ ]
110
+ local_time = Time.parse('2012-06-21 03:12:00').to_i
111
+ assert_equal 'foo-2012-06-21-03-12-00', driver.instance.patterns[0].get_count_key(local_time)
112
+ end
113
+
114
+ def test_configure_duplicated_timezone
115
+ begin
116
+ create_driver %[
117
+ <pattern>
118
+ count_key_format foo%Y
119
+ localtime
120
+ utc
121
+ </pattern>
122
+ ]
123
+ flunk
124
+ rescue Fluent::ConfigError => e
125
+ assert_equal 'both "localtime" and "utc" are specified.', e.message
74
126
  end
75
127
  end
76
128
 
@@ -89,8 +141,9 @@ class RedisCounterTest < Test::Unit::TestCase
89
141
  end
90
142
 
91
143
  def test_format
92
- @d.emit({"a" => 1})
93
- @d.expect_format({"a" => 1}.to_msgpack)
144
+ time = Time.parse('2012-06-21 01:55:00 UTC').to_i
145
+ @d.emit({"a" => 1}, time)
146
+ @d.expect_format(['test', time, {"a" => 1}].to_msgpack)
94
147
  @d.run
95
148
  end
96
149
 
@@ -114,4 +167,20 @@ class RedisCounterTest < Test::Unit::TestCase
114
167
  assert_nil driver.instance.redis.get("b")
115
168
  end
116
169
 
170
+ def test_write_with_timeformat
171
+ driver = create_driver %[
172
+ db_number 1
173
+ <pattern>
174
+ match_a ^2[0-9][0-9]$
175
+ count_key_format foo-%Y-%m-%d
176
+ count_value 2
177
+ </pattern>
178
+ ]
179
+ time = Time.parse('2012-06-21 03:01:00 UTC').to_i
180
+ driver.emit({"a" => "200"}, time)
181
+ driver.run
182
+
183
+ assert_equal '2', driver.instance.redis.get("foo-2012-06-21")
184
+ end
185
+
117
186
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-redis-counter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-19 00:00:00.000000000 Z
12
+ date: 2012-06-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fluentd