fluent-mixin-elapsed_time 0.0.4

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.
@@ -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