fluent-plugin-lossycount 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjA2OWZjMWIwY2QxZTE0OTI2NGMzODkxMjJiYjg4OGQ2Mzg0YjZhYQ==
5
+ data.tar.gz: !binary |-
6
+ MmFiMTk3Nzk0ODZlZTY3YTViMmQ4NjU1MWQ0ZTk1ZjhiYzEwYjRkZA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZGZiM2M2Y2MzZjk4NDk2NTU4ZjMwZmQyYzMxMzhiNWEwMTViNGNkZTAzZTYx
10
+ ZjViNjRkZTExNDY2YzMzNmI1Y2EzODIyMGIwNTFjNTFiZmZhYTQzZjE1YTE4
11
+ ZWY3MDk0ZmYyZDUzNjI4MTEyMTc2MDRiZWM1OWJhYTVlYzYwYTA=
12
+ data.tar.gz: !binary |-
13
+ Y2EyMzg0ZjcwYzQ3ZWMzNmZlNTZhMTZjMDI1MTEyZWVlZGJlMjYyZTY0MWU1
14
+ OTMzNzQ1NTE2MDk3YWU2ZmViMzlmNThmYzk0MjQzYjNhZTdkZDg3ODAyMWM1
15
+ NzM5YmE3OGFmY2U2YWFhNjFiMmYzZTVhNTExNmEwOWM0MjRiN2M=
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ source
3
+ dump.rdb
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ moaikids
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,30 @@
1
+ = Lossy count output plugin for Fluentd
2
+
3
+ == Overview
4
+
5
+ *lossycount* output plugin is a counter logic.
6
+
7
+ == Installation
8
+
9
+ == Configuration
10
+
11
+ <match pattern>
12
+ type lossycount
13
+ gamma LOSSY_COUNT_GAMMA
14
+ epsilon LOSSY_COUNT_EPSILON
15
+ key_name INPUT_KEY_NAME
16
+ time_windows SECONDS
17
+ output_tag OUTPUT_TAG_NAME
18
+ output_key_name OUTPUT_KEY_NAME
19
+ output_timestamp_name OUTPUT_TIMESTAMP_NAME
20
+ output_value_name OUTPUT_VALUE_NAME
21
+ enable_metrics true
22
+ metrics_tag METRICS_OUTPUT_TAG_NAME
23
+ verbose false
24
+ </match>
25
+
26
+ == Copyright
27
+
28
+ Copyright:: Copyright (c) 2013 moaikids
29
+ License:: Apache License, Version 2.0
30
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |gem|
3
+ gem.name = "fluent-plugin-lossycount"
4
+ gem.version = "0.0.1"
5
+ gem.authors = ["moaikids"]
6
+ gem.licenses = ["Apache License Version 2.0"]
7
+ gem.summary = %q{Lossy count output plugin for Fluentd}
8
+ gem.description = %q{Lossy count output plugin for Fluentd...}
9
+ gem.homepage = "https://github.com/moaikids/fluent-plugin-lossycount"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.require_paths = ["lib"]
15
+
16
+ gem.add_development_dependency "rake"
17
+ gem.add_development_dependency "shoulda"
18
+ gem.add_runtime_dependency "fluentd"
19
+ end
@@ -0,0 +1,195 @@
1
+ class Fluent::LossyCountOutput < Fluent::Output
2
+ Fluent::Plugin.register_output('lossycount', self)
3
+
4
+ config_param :key_name, :string, :default => nil
5
+ config_param :time_windows, :time, :default => 60
6
+ config_param :output_tag, :string, :default => nil
7
+ config_param :output_key_name, :string, :default => 'key'
8
+ config_param :output_timestamp_name, :string, :default => 'timestamp'
9
+ config_param :output_value_name, :string, :default => 'value'
10
+ config_param :gamma, :float, :default => 0.005
11
+ config_param :epsilon, :float, :default => 0.004
12
+ config_param :enable_metrics, :bool, :default => false
13
+ config_param :metrics_tag, :string, :default => 'counter.metrics'
14
+ config_param :verbose, :bool, :default => false
15
+
16
+ def configure(config)
17
+ super
18
+ unless config.has_key?('key_name')
19
+ raise Fluent::ConfigError, "you must set 'key_name'"
20
+ end
21
+ unless config.has_key?('output_tag')
22
+ raise Fluent::ConfigError, "you must set 'output_tag'"
23
+ end
24
+
25
+ @mutex = Mutex.new
26
+ @sleep_wait = 0.4
27
+ init_counter()
28
+ end
29
+
30
+ def start
31
+ super
32
+ start_watch
33
+ end
34
+
35
+ def shutdown
36
+ super
37
+ @watcher.terminate
38
+ @watcher.join
39
+ end
40
+
41
+ def init_counter()
42
+ @counter = Counter::LossyCounter.new({:gamma => @gamma, :epsilon => @epsilon})
43
+ end
44
+
45
+ def flush_counter(step)
46
+ freq_counter = {}
47
+ metrics = {}
48
+ @mutex.synchronize {
49
+ freq_counter = @counter.get()
50
+ if @enable_metrics
51
+ metrics = @counter.get_metrics()
52
+ end
53
+ init_counter()
54
+ }
55
+ flush_time = Fluent::Engine.now.to_i
56
+ if @verbose
57
+ $log.info "flushtime : " + flush_time.to_s
58
+ $log.info "{ "
59
+ end
60
+ freq_counter.each_pair { |key, value|
61
+ map = {@output_key_name => key, @output_timestamp_name => flush_time, @output_value_name => value}
62
+ if @verbose
63
+ $log.info map
64
+ end
65
+ Fluent::Engine.emit(@output_tag, Fluent::Engine.now, map)
66
+ }
67
+ if @verbose
68
+ $log.info "}"
69
+ end
70
+
71
+ if @enable_metrics
72
+ if @verbose
73
+ $log.info "metrics : " + metrics.to_s
74
+ end
75
+ Fluent::Engine.emit(@metrics_tag, Fluent::Engine.now, metrics)
76
+ end
77
+ end
78
+
79
+ def start_watch
80
+ @watcher = Thread.new{
81
+ @last_checked = Fluent::Engine.now.to_i
82
+ while true
83
+ sleep @sleep_wait
84
+ now = Fluent::Engine.now.to_i
85
+ if now - @last_checked >= @time_windows
86
+ flush_counter(now - @last_checked)
87
+ @last_checked = now
88
+ end
89
+ end
90
+ }
91
+ end
92
+
93
+ def emit(tag, es, chain)
94
+ es.each {|time,record|
95
+ k = traverse(record, @key_name)
96
+ if k
97
+ if k.is_a?(Array)
98
+ k.each{ |v|
99
+ @counter.add(v.to_s)
100
+ }
101
+ else
102
+ @counter.add(k.to_s)
103
+ end
104
+ end
105
+ }
106
+ chain.next
107
+ end
108
+
109
+ def traverse(data, key)
110
+ val = data
111
+ key.split('.').each{ |k|
112
+ if val.has_key?(k)
113
+ val = val[k]
114
+ else
115
+ return nil
116
+ end
117
+ }
118
+ return val
119
+ end
120
+ end
121
+
122
+
123
+ module Counter
124
+ class LossyCounter
125
+ def initialize(config)
126
+ @gamma = config.has_key?(:gamma) ? config[:gamma].to_f : 0.005
127
+ @epsilon = config.has_key?(:epsilon) ? config[:epsilon].to_f : 0.001
128
+ @current = 1
129
+ @freq_counter = {}
130
+ @delta_counter = {}
131
+ @num = 0
132
+ @max_size = -1
133
+ end
134
+
135
+ def add(key)
136
+ if @freq_counter.has_key?(key)
137
+ @freq_counter[key] += 1
138
+ else
139
+ @freq_counter[key] = 1
140
+ @delta_counter[key] = @current - 1
141
+ end
142
+ @num += 1
143
+ if @num % (1 / @epsilon).to_i == 0
144
+ sweep()
145
+ end
146
+ end
147
+
148
+ def sweep()
149
+ length = @freq_counter.length
150
+ if @max_size < length
151
+ @max_size = length
152
+ end
153
+
154
+ @freq_counter.each_pair { |key, value|
155
+ if value <= (@current - @delta_counter[key])
156
+ @freq_counter.delete(key)
157
+ @delta_counter.delete(key)
158
+ end
159
+ }
160
+ @current += 1
161
+ end
162
+
163
+ def get()
164
+ buf = {}
165
+ @freq_counter.each_pair { |key, value|
166
+ if value > (@num * (@gamma - @epsilon) ).to_i
167
+ buf[key] = value
168
+ end
169
+ }
170
+ return buf
171
+ end
172
+
173
+ def get_num()
174
+ return @num
175
+ end
176
+
177
+ def get_current_max_size()
178
+ return @max_size
179
+ end
180
+
181
+ def get_num_x_gamma()
182
+ return @num.to_f * @gamma
183
+ end
184
+
185
+ def get_num_x_gamma_d_epsilon()
186
+ return @num.to_f * (@gamma - @epsilon)
187
+ end
188
+
189
+ def get_metrics()
190
+ return {'num' => get_num(), 'max_size' => get_current_max_size(), 'current_size' => @freq_counter.size() , 'reduced_size' => get().size(), "gamma" => @gamma , "epsilon" => @epsilon , "n_x_gamma" => get_num_x_gamma(), "n_x_gamma-epsilon" => get_num_x_gamma_d_epsilon() }
191
+ end
192
+
193
+ end
194
+ end
195
+
@@ -0,0 +1,73 @@
1
+ require 'helper'
2
+
3
+ class LossyCounterTest < Test::Unit::TestCase
4
+ def setup
5
+ @gamma = 0.005
6
+ @epsilon = 0.0045
7
+ end
8
+
9
+ def test_add_naive()
10
+ counter = Counter::LossyCounter.new({:gamma => @gamma, :epsilon => @epsilon})
11
+ counter.add('a')
12
+ counter.add('b')
13
+ counter.add('c')
14
+ counter.add('b')
15
+ counter.add('a')
16
+ counter.add('b')
17
+
18
+ freq_counter = counter.get()
19
+
20
+ p freq_counter
21
+ assert_equal freq_counter.has_key?('a') , true
22
+ assert_equal freq_counter.has_key?('b') , true
23
+ assert_equal freq_counter.has_key?('c') , true
24
+ assert_equal freq_counter.has_key?('d') , false
25
+ assert_equal freq_counter['a'] , 2
26
+ assert_equal freq_counter['b'] , 3
27
+ assert_equal freq_counter['c'] , 1
28
+ assert_equal freq_counter['d'] , nil
29
+
30
+ p counter.get_metrics()
31
+ end
32
+
33
+ def test_add_and_sweep()
34
+ counter = Counter::LossyCounter.new({:gamma => @gamma, :epsilon => @epsilon})
35
+ buf = {}
36
+ radix = [10000000, 1000000, 100000, 10000, 1000, 100, 10]
37
+ l = 100000
38
+
39
+ (0..(l - 1)).step(1) do |i|
40
+ now = ((Time.now.to_f * 10000000) % 10000000).to_i
41
+ for r in radix
42
+ key = now & r
43
+ counter.add(key.to_s)
44
+ if buf.has_key?(key.to_s)
45
+ buf[key] += 1
46
+ else
47
+ buf[key] = 1
48
+ end
49
+ end
50
+ end
51
+
52
+ n = counter.get_num()
53
+ g = counter.get_num_x_gamma().to_i
54
+ e = (n * @epsilon).to_i
55
+ ge = counter.get_num_x_gamma_d_epsilon().to_i
56
+ freq_count = counter.get()
57
+
58
+ p counter.get_metrics()
59
+
60
+ buf.each_pair { |key, value|
61
+ if freq_count.has_key?(key)
62
+ if freq_count[key] >= g.to_i
63
+ assert_equal value , freq_count[key]
64
+ else
65
+ assert_operator e.to_i , :>= , (freq_count[key] - value).abs
66
+ end
67
+ else
68
+ assert_operator e.to_i , :>= , value
69
+ end
70
+ }
71
+
72
+ end
73
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ require 'test/unit'
12
+ require 'shoulda'
13
+
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ require 'fluent/test'
17
+ require 'fluent/plugin/out_lossycount'
18
+
19
+ class Test::Unit::TestCase
20
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-lossycount
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - moaikids
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: shoulda
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fluentd
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Lossy count output plugin for Fluentd...
56
+ email:
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - .gitignore
62
+ - AUTHORS
63
+ - Gemfile
64
+ - README.rdoc
65
+ - Rakefile
66
+ - fluent-plugin-lossycount.gemspec
67
+ - lib/fluent/plugin/out_lossycount.rb
68
+ - test/counter/test_lossy_counter.rb
69
+ - test/helper.rb
70
+ homepage: https://github.com/moaikids/fluent-plugin-lossycount
71
+ licenses:
72
+ - Apache License Version 2.0
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.0.7
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Lossy count output plugin for Fluentd
94
+ test_files:
95
+ - test/counter/test_lossy_counter.rb
96
+ - test/helper.rb