fluent-mixin-elapsed_time 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 533cc98faf8bd7e5db959f74dcdb29a1f9142f37
4
+ data.tar.gz: 9b86326b5eeaf48fcca0a058cbcc48098655d668
5
+ SHA512:
6
+ metadata.gz: 4a5f59e3baee45cbf8a6dc02933a37eb3d88508c8f1fb567ab825b2fdce8013a8a0c45dadb8fa98634a3a31f15f175a271d859fb83b573495088f7615d4915eb
7
+ data.tar.gz: cda37c465ec1986a4aae194cb0e7e46a2c4983f0d6baa3be8408091b9c79f0baf5921b14b3b8b47594b201ef0945f9356747070b9a6491fbe9768da9b4dc43d5
@@ -0,0 +1,14 @@
1
+ /*.gem
2
+ ~*
3
+ #*
4
+ *~
5
+ .bundle
6
+ Gemfile.lock
7
+ .rbenv-version
8
+ vendor
9
+ doc/*
10
+ tmp/*
11
+ coverage
12
+ .yardoc
13
+ pkg/
14
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
5
+ gemfile:
6
+ - Gemfile
@@ -0,0 +1,17 @@
1
+ ## 0.0.4 (2014/04/10)
2
+
3
+ Fixes:
4
+
5
+ * Fix to apply only plugins having <elapsed></elapsed> directive
6
+
7
+ ## 0.0.3 (2014/04/08)
8
+
9
+ Enhancements:
10
+
11
+ * Support to hook Output plugins
12
+
13
+ ## 0.0.2 (2014/04/06)
14
+
15
+ First release
16
+
17
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Naotoshi SEO
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,79 @@
1
+ # fluent-mixin-elapsed_time
2
+
3
+ [![Build Status](https://secure.travis-ci.org/sonots/fluent-mixin-elapsed_time.png?branch=master)](http://travis-ci.org/sonots/fluent-mixin-elapsed_time)
4
+ [![Code Climate](https://codeclimate.com/github/sonots/fluent-mixin-elapsed_time.png)](https://codeclimate.com/github/sonots/fluent-mixin-elapsed_time)
5
+
6
+ Fluentd mixin to measure elapsed time to process messages
7
+
8
+ ## Installation
9
+
10
+ Use RubyGems:
11
+
12
+ gem install fluent-mixin-elapsed_time
13
+
14
+ Run Fluentd with -r option to require this gem. This will automatically extends all input and output plugins (actually, `Input` and `Output` base class).
15
+
16
+ fluentd -c fluent.conf -r 'fluent/mixin/elapsed_time'
17
+
18
+ ## Configuration
19
+
20
+ This mixin module extends arbitrary plugins so that it can use `<elapsed></elapsed>` directive to measure elapsed times.
21
+
22
+ Example:
23
+
24
+ ```apache
25
+ <source>
26
+ type forward
27
+ port 24224
28
+ <elapsed>
29
+ tag elapsed
30
+ interval 60
31
+ hook on_message
32
+ </elapsed>
33
+ </source>
34
+
35
+ <match elapsed>
36
+ type stdout
37
+ </match>
38
+ ```
39
+
40
+ This example hooks the [on_message](https://github.com/fluent/fluentd/blob/e5a9a4ca03d18b45fdb89061d8251592a044e9fc/lib/fluent/plugin/in_forward.rb#L112) method of in_forward plugin, and measures how long it takes for processing.
41
+
42
+ And, this plugin emits the statistics of measured elapsed times in each specified interval like below:
43
+
44
+ ```
45
+ elapsed: {"max":1.011,"avg":0.002","num":10}
46
+ ```
47
+
48
+ where `max` and `avg` are the maximum and average elapsed times, and `num` is the number of being called in each interval.
49
+
50
+ ## Parameters
51
+
52
+ * interval
53
+
54
+ The time interval to emit measurement results. Default is `60`.
55
+
56
+ * tag
57
+
58
+ The output tag name. Default is `elapsed`
59
+
60
+ * hook (required)
61
+
62
+ Specify the method to hook. You can also explicitly specify the class name like `Fluent::ForwardOutput.on_message`.
63
+ (EXCUSE: Fluentd treats strings after # as a comment, so the form like `Fluent::ForwardInput#on_message` could not be used)
64
+
65
+ ## ChangeLog
66
+
67
+ See [CHANGELOG.md](CHANGELOG.md) for details.
68
+
69
+ ## Contributing
70
+
71
+ 1. Fork it
72
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
73
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
74
+ 4. Push to the branch (`git push origin my-new-feature`)
75
+ 5. Create new [Pull Request](../../pull/new/master)
76
+
77
+ ## Copyright
78
+
79
+ Copyright (c) 2014 Naotoshi Seo. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core'
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = FileList['spec/**/*_spec.rb']
8
+ end
9
+ task :default => :spec
10
+
11
+ desc 'Open an irb session preloaded with the gem library'
12
+ task :console do
13
+ sh 'irb -rubygems -I lib'
14
+ end
15
+ task :c => :console
@@ -0,0 +1,12 @@
1
+ <source>
2
+ type forward
3
+ log_level info
4
+ <elapsed>
5
+ tag elapsed
6
+ interval 1s
7
+ hook Fluent::ForwardInput::Handler.on_read
8
+ </elapsed>
9
+ </source>
10
+ <match **>
11
+ type stdout
12
+ </match>
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "fluent-mixin-elapsed_time"
6
+ gem.version = "0.0.4"
7
+ gem.authors = ["Naotoshi Seo"]
8
+ gem.email = "sonots@gmail.com"
9
+ gem.homepage = "https://github.com/sonots/fluent-mixin-elapsed_time"
10
+ gem.description = "Fluentd mixin to measure elapsed time to process messages"
11
+ gem.summary = gem.description
12
+ gem.licenses = ["MIT"]
13
+ gem.has_rdoc = false
14
+
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency "fluentd", "~> 0.10.17"
21
+ gem.add_development_dependency "rake"
22
+ gem.add_development_dependency "rspec"
23
+ gem.add_development_dependency "pry"
24
+ gem.add_development_dependency "pry-nav"
25
+ end
@@ -0,0 +1,168 @@
1
+ require 'fluent/input'
2
+
3
+ module Fluent
4
+ module ElapsedTimable
5
+ def self.included(klass)
6
+ klass.__send__(:alias_method, :configure_without_elapsed, :configure)
7
+ klass.__send__(:alias_method, :configure, :configure_with_elapsed)
8
+ end
9
+
10
+ def configure_with_elapsed(conf)
11
+ configure_without_elapsed(conf)
12
+ if element = conf.elements.select { |element| element.name == 'elapsed' }.first
13
+ @elapsed = ElapsedTime.new(self, log)
14
+ @elapsed.configure(element)
15
+ # #start and #stop methods must be extended in concrete input plugins
16
+ # because most of built-in input plugins do not call `super`
17
+ klass = self.class
18
+ unless klass.method_defined?(:start_without_elapsed)
19
+ klass.__send__(:alias_method, :start_without_elapsed, :start)
20
+ klass.__send__(:alias_method, :start, :start_with_elapsed)
21
+ klass.__send__(:alias_method, :shutdown_without_elapsed, :shutdown)
22
+ klass.__send__(:alias_method, :shutdown, :shutdown_with_elapsed)
23
+ end
24
+ end
25
+ end
26
+
27
+ def elapsed
28
+ @elapsed
29
+ end
30
+
31
+ def start_with_elapsed
32
+ start_without_elapsed
33
+ @elapsed.start if @elapsed
34
+ end
35
+
36
+ def shutdown_with_elapsed
37
+ shutdown_without_elapsed
38
+ @elapsed.stop if @elapsed
39
+ end
40
+ end
41
+
42
+ Input.__send__(:include, ElapsedTimable)
43
+ Output.__send__(:include, ElapsedTimable)
44
+
45
+ class ElapsedTime
46
+ attr_reader :input, :log, :times, :mutex, :thread, :tag, :interval, :hook
47
+ def initialize(input, log)
48
+ @input = input
49
+ @log = log
50
+ @times = []
51
+ @mutex = Mutex.new
52
+ end
53
+
54
+ def configure(conf)
55
+ @tag = conf['tag'] || 'elapsed'
56
+ @interval = conf['interval'].to_i || 60
57
+ unless @hook = conf['hook']
58
+ raise Fluent::ConfigError, '`hook` option must be specified in <elapsed></elpased> directive'
59
+ end
60
+ apply_hook(@hook)
61
+ end
62
+
63
+ def add(time)
64
+ @times << time
65
+ end
66
+
67
+ def clear
68
+ @times.clear
69
+ end
70
+
71
+ def apply_hook(hook)
72
+ if hook.include?('.')
73
+ klass_name, method_name = hook.split('.', 2)
74
+ klass = constantize(klass_name)
75
+ else
76
+ klass = @input.class
77
+ method_name = hook
78
+ end
79
+ old_method_name = "#{method_name}_without_elapsed".to_sym
80
+ unless klass.method_defined?(old_method_name)
81
+ klass.__send__(:alias_method, old_method_name, method_name)
82
+ klass.__send__(:define_method, method_name) do |*args|
83
+ elapsed.measure_time(klass, method_name) do
84
+ self.__send__(old_method_name, *args)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def measure_time(klass, method_name)
91
+ started = Time.now
92
+ output = yield
93
+ elapsed = (Time.now - started).to_f
94
+ log.info "elapsed time at #{klass}##{method_name} is #{elapsed} sec"
95
+ @mutex.synchronize { self.add(elapsed) }
96
+ output
97
+ end
98
+
99
+ def start
100
+ @thread = Thread.new(&method(:run))
101
+ end
102
+
103
+ def stop
104
+ @thread.terminate
105
+ @thread.join
106
+ end
107
+
108
+ def run
109
+ @last_checked ||= Engine.now
110
+ while (sleep 0.5)
111
+ begin
112
+ now = Engine.now
113
+ if now - @last_checked >= @interval
114
+ flush(now)
115
+ @last_checked = now
116
+ end
117
+ rescue => e
118
+ log.warn "in_forward: #{e.class} #{e.message} #{e.backtrace.first}"
119
+ end
120
+ end
121
+ end
122
+
123
+ def flush(now)
124
+ times = []
125
+ @mutex.synchronize do
126
+ times = @times.dup
127
+ self.clear
128
+ end
129
+ triple = nil
130
+ unless times.empty?
131
+ num = times.size
132
+ max = num == 0 ? 0 : times.max
133
+ avg = num == 0 ? 0 : times.map(&:to_f).inject(:+) / num.to_f
134
+ triple = [@tag, now, {:num => num, :max => max, :avg => avg}]
135
+ Engine.emit(*triple)
136
+ end
137
+ triple
138
+ end
139
+
140
+ private
141
+ # File activesupport/lib/active_support/inflector/methods.rb, line 219
142
+ def constantize(camel_cased_word)
143
+ names = camel_cased_word.split('::')
144
+ names.shift if names.empty? || names.first.empty?
145
+
146
+ names.inject(Object) do |constant, name|
147
+ if constant == Object
148
+ constant.const_get(name)
149
+ else
150
+ candidate = constant.const_get(name)
151
+ next candidate if constant.const_defined?(name, false)
152
+ next candidate unless Object.const_defined?(name)
153
+
154
+ # Go down the ancestors to check it it's owned
155
+ # directly before we reach Object or the end of ancestors.
156
+ constant = constant.ancestors.inject do |const, ancestor|
157
+ break const if ancestor == Object
158
+ break ancestor if ancestor.const_defined?(name, false)
159
+ const
160
+ end
161
+
162
+ # owner is in Object, so raise
163
+ constant.const_get(name, false)
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,117 @@
1
+ # encoding: UTF-8
2
+ require_relative 'spec_helper'
3
+ require 'fluent/plugin/in_forward'
4
+ require 'fluent/plugin/out_stdout'
5
+
6
+ describe Fluent::ForwardInput do
7
+ before { Fluent::Test.setup }
8
+
9
+ def create_driver(conf=CONFIG)
10
+ Fluent::Test::InputTestDriver.new(Fluent::ForwardInput).configure(conf)
11
+ end
12
+
13
+ def connect
14
+ TCPSocket.new('127.0.0.1', PORT)
15
+ end
16
+
17
+ def send_data(data)
18
+ io = connect
19
+ begin
20
+ io.write data
21
+ ensure
22
+ io.close
23
+ end
24
+ end
25
+
26
+ def self.unused_port
27
+ s = TCPServer.open(0)
28
+ port = s.addr[1]
29
+ s.close
30
+ port
31
+ end
32
+
33
+ PORT = unused_port
34
+ CONFIG = %[
35
+ port #{PORT}
36
+ bind 127.0.0.1
37
+ ]
38
+
39
+ let(:driver) { create_driver(config) }
40
+
41
+ describe 'test configure' do
42
+ let(:config) {CONFIG + %[
43
+ <elapsed>
44
+ tag test
45
+ interval 10
46
+ hook on_message
47
+ </elapsed>
48
+ ]}
49
+ let(:subject) { driver.instance.elapsed }
50
+ its(:tag) { should == 'test' }
51
+ its(:interval) { should == 10 }
52
+ its(:hook) { should == 'on_message' }
53
+ end
54
+
55
+ describe 'test emit' do
56
+ let(:config) {CONFIG + %[
57
+ <elapsed>
58
+ tag elapsed
59
+ interval 1
60
+ # hook Fluent::ForwardInput::Handler.on_read # not support inner class yet
61
+ hook Fluent::ForwardInput.on_message
62
+ </elapsed>
63
+ ]}
64
+ it 'should flush' do
65
+ d = driver.instance
66
+ d.__send__(:on_message, ['tag1', 0, {'a'=>1}].to_msgpack)
67
+ triple = d.elapsed.flush(0)
68
+ triple[0].should == 'elapsed'
69
+ triple[2].keys.should =~ [:num, :max, :avg]
70
+ end
71
+ end
72
+ end
73
+
74
+ describe Fluent::StdoutOutput do
75
+ before { Fluent::Test.setup }
76
+
77
+ def create_driver(conf=CONFIG, tag = 'test')
78
+ Fluent::Test::OutputTestDriver.new(Fluent::StdoutOutput, tag).configure(conf)
79
+ end
80
+
81
+ CONFIG = %[
82
+ ]
83
+
84
+ let(:driver) { create_driver(config) }
85
+
86
+ describe 'test configure' do
87
+ let(:config) {CONFIG + %[
88
+ <elapsed>
89
+ tag test
90
+ interval 10
91
+ hook emit
92
+ </elapsed>
93
+ ]}
94
+ let(:subject) { driver.instance.instance_variable_get(:@elapsed) }
95
+ its(:tag) { should == 'test' }
96
+ its(:interval) { should == 10 }
97
+ its(:hook) { should == 'emit' }
98
+ end
99
+
100
+ describe 'test emit' do
101
+ let(:config) {CONFIG + %[
102
+ <elapsed>
103
+ tag elapsed
104
+ interval 1
105
+ hook emit
106
+ </elapsed>
107
+ ]}
108
+ it 'should flush' do
109
+ d = driver.instance
110
+ d.emit('tag1', Fluent::OneEventStream.new(0, {'a'=>1}), Fluent::NullOutputChain.instance)
111
+ triple = d.elapsed.flush(0)
112
+ triple[0].should == 'elapsed'
113
+ triple[2].keys.should =~ [:num, :max, :avg]
114
+ end
115
+ end
116
+ end
117
+
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ require 'fluent/load'
5
+ Bundler.setup(:default, :test)
6
+ Bundler.require(:default, :test)
7
+
8
+ require 'fluent/test'
9
+ require 'rspec'
10
+ require 'pry'
11
+
12
+ $TESTING=true
13
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
14
+ require 'fluent/mixin/elapsed_time'
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-mixin-elapsed_time
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Naotoshi Seo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fluentd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.10.17
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.10.17
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
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: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-nav
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Fluentd mixin to measure elapsed time to process messages
84
+ email: sonots@gmail.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - .gitignore
90
+ - .rspec
91
+ - .travis.yml
92
+ - CHANGELOG.md
93
+ - Gemfile
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - examples/fluent.conf
98
+ - fluent-mixin-elapsed-time.gemspec
99
+ - lib/fluent/mixin/elapsed_time.rb
100
+ - spec/elapsed_time_spec.rb
101
+ - spec/spec_helper.rb
102
+ homepage: https://github.com/sonots/fluent-mixin-elapsed_time
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.0.3
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Fluentd mixin to measure elapsed time to process messages
126
+ test_files:
127
+ - spec/elapsed_time_spec.rb
128
+ - spec/spec_helper.rb