fluent-plugin-measure_time 0.1.0
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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +108 -0
- data/Rakefile +15 -0
- data/benchmark/Gemfile +7 -0
- data/benchmark/README.md +93 -0
- data/benchmark/agent.conf +24 -0
- data/benchmark/dummer.conf +4 -0
- data/benchmark/hooked_in_forward.conf +19 -0
- data/benchmark/in_forward.conf +8 -0
- data/benchmark/patched_in_forward.conf +16 -0
- data/benchmark/plugin/in_forward.rb +345 -0
- data/fluent-plugin-measure_time.gemspec +25 -0
- data/lib/fluent/plugin/in_measure_time.rb +135 -0
- data/spec/in_measure_time_spec.rb +129 -0
- data/spec/spec_helper.rb +14 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 193e1f487b71b8b3e05144ada9ef36651b95d230
|
4
|
+
data.tar.gz: d0a5363194ca0c40b26d242dff97d703e7850c11
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c76cda2d36d046e9496509fe3f139394660b1e2791294489bcfbb1c78706af03830886e972f081ef6c07a77512d059a12b7a405ddfd64e49c35f93eeb204b038
|
7
|
+
data.tar.gz: cbdd31735b323db8d30d5292b66cf939131f491b07968312f00691684859310722d497c3cc20ae55c87fc3012414e2c672db4e1d4eb70b1d1ec4650bf49a2d1c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
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.
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# fluent-plugin-measure_time
|
2
|
+
|
3
|
+
[](http://travis-ci.org/sonots/fluent-plugin-measure_time)
|
4
|
+
[](https://codeclimate.com/github/sonots/fluent-plugin-measure_time)
|
5
|
+
|
6
|
+
Fluentd plugin to measure elapsed time to process messages
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Use RubyGems:
|
11
|
+
|
12
|
+
gem install fluent-plugin-measure_time
|
13
|
+
|
14
|
+
## Configuration
|
15
|
+
|
16
|
+
This plugin is doing something tricky, which extends arbitrary plugins so that it can use `<measure_time></measure_time>` directive to measure elapsed times.
|
17
|
+
|
18
|
+
Example:
|
19
|
+
|
20
|
+
```apache
|
21
|
+
<source>
|
22
|
+
type measure_time
|
23
|
+
# This makes available the `masure_time` directive for all plugins
|
24
|
+
</source>
|
25
|
+
|
26
|
+
<source>
|
27
|
+
type forward
|
28
|
+
port 24224
|
29
|
+
<measure_time>
|
30
|
+
tag measure_time
|
31
|
+
hook on_message
|
32
|
+
</measure_time>
|
33
|
+
</source>
|
34
|
+
|
35
|
+
<match measure_time>
|
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. Output becomes as below:
|
41
|
+
|
42
|
+
```
|
43
|
+
measure_time: {"time":0.000849735,"class":"Fluent::ForwardInput","hook":"on_message","object_id":83935080}
|
44
|
+
```
|
45
|
+
|
46
|
+
where `time` denotes the measured elapsed time, and `class`, `hook`, and `object_id` denotes the hooked class, the hooked method, and the object id of the plugin instance.
|
47
|
+
|
48
|
+
Example: interval
|
49
|
+
|
50
|
+
With `interval` option, this plugin compute statistics of measured elapsed times in each interval
|
51
|
+
|
52
|
+
```apache
|
53
|
+
<source>
|
54
|
+
type measure_time
|
55
|
+
</source>
|
56
|
+
|
57
|
+
<source>
|
58
|
+
type forward
|
59
|
+
port 24224
|
60
|
+
<measure_time>
|
61
|
+
tag measure_time
|
62
|
+
interval 60
|
63
|
+
hook on_message
|
64
|
+
</measure_time>
|
65
|
+
</source>
|
66
|
+
|
67
|
+
<match measure_time>
|
68
|
+
type stdout
|
69
|
+
</match>
|
70
|
+
```
|
71
|
+
|
72
|
+
Output becomes as below:
|
73
|
+
|
74
|
+
```
|
75
|
+
measure_time: {"max":1.011,"avg":0.002","num":10,"class":"Fluent::ForwardInput","hook":"on_message","object_id":83935080}
|
76
|
+
```
|
77
|
+
|
78
|
+
where `max` and `avg` are the maximum and average elapsed times, and `num` is the number of being called in each interval.
|
79
|
+
|
80
|
+
## Parameters
|
81
|
+
|
82
|
+
* tag
|
83
|
+
|
84
|
+
The output tag name. Default is `measure_time`
|
85
|
+
|
86
|
+
* hook (required)
|
87
|
+
|
88
|
+
Specify the method to measure time.
|
89
|
+
|
90
|
+
* interval
|
91
|
+
|
92
|
+
The time interval to emit measurement results. Default is nil which do not compute statistics and emit the time in each measurement.
|
93
|
+
|
94
|
+
## ChangeLog
|
95
|
+
|
96
|
+
See [CHANGELOG.md](CHANGELOG.md) for details.
|
97
|
+
|
98
|
+
## Contributing
|
99
|
+
|
100
|
+
1. Fork it
|
101
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
102
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
103
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
104
|
+
5. Create new [Pull Request](../../pull/new/master)
|
105
|
+
|
106
|
+
## Copyright
|
107
|
+
|
108
|
+
Copyright (c) 2014 Naotoshi Seo. See [LICENSE](LICENSE) for details.
|
data/Rakefile
ADDED
@@ -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
|
data/benchmark/Gemfile
ADDED
data/benchmark/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# Fluentd benchmark - forward
|
2
|
+
|
3
|
+
This benchmarks following architecture scenario:
|
4
|
+
|
5
|
+
```
|
6
|
+
Agent Node Receiver Node
|
7
|
+
+-----------------------------------+ +-----------------+
|
8
|
+
| +-----------+ +-----------+ | | +-----------+ |
|
9
|
+
| | | | | | | | | |
|
10
|
+
| | Log File +----->| Fluentd +--------------->| Fluentd | |
|
11
|
+
| | | | | | | | | |
|
12
|
+
| +-----------+ in_tail ----- out_forward in_forward -----+ |
|
13
|
+
+-----------------------------------+ +-----------------+
|
14
|
+
```
|
15
|
+
|
16
|
+
## Setup Fluentd Receiver
|
17
|
+
|
18
|
+
Assum ruby is installed
|
19
|
+
|
20
|
+
```
|
21
|
+
git clone https://github.com/sonots/fluentd-benchmark
|
22
|
+
cd fluentd-benchmark/one_forward
|
23
|
+
bundle
|
24
|
+
bundle exec fluentd -c receiver.conf
|
25
|
+
```
|
26
|
+
|
27
|
+
## Setup Fluentd Agent
|
28
|
+
|
29
|
+
Assume ruby is installed
|
30
|
+
|
31
|
+
```
|
32
|
+
git clone https://github.com/sonots/fluentd-benchmark
|
33
|
+
cd fluentd-benchmark/one_forward
|
34
|
+
bundle
|
35
|
+
bundle exec fluentd -c agent.conf
|
36
|
+
```
|
37
|
+
|
38
|
+
## Run benchmark tool and measure
|
39
|
+
|
40
|
+
Run at Fluentd agent server.
|
41
|
+
|
42
|
+
This tool generates a log file to dummy.log and Fluentd agent will read and send data to receiver.
|
43
|
+
|
44
|
+
```
|
45
|
+
cd fluentd-benchmark/one_forward
|
46
|
+
bundle exec dummer -c dummer.conf
|
47
|
+
```
|
48
|
+
|
49
|
+
You may increase the rate (messages/sec) of generating log by -r option to benchmark.
|
50
|
+
|
51
|
+
```
|
52
|
+
bundle exec dummer -c dummer.conf -r 100000
|
53
|
+
```
|
54
|
+
|
55
|
+
You should see an output on Fluentd receiver as following. This will tell you the performance of fluentd processing.
|
56
|
+
|
57
|
+
```
|
58
|
+
2014-02-20 17:20:55 +0900 [info]: plugin:out_flowcounter_simple count:500 indicator:num unit:second
|
59
|
+
2014-02-20 17:20:56 +0900 [info]: plugin:out_flowcounter_simple count:500 indicator:num unit:second
|
60
|
+
2014-02-20 17:20:57 +0900 [info]: plugin:out_flowcounter_simple count:500 indicator:num unit:second
|
61
|
+
```
|
62
|
+
|
63
|
+
You may use `iostat -dkxt 1`, `vmstat 1`, `top -c`, `free`, or `dstat` commands to measure system resources.
|
64
|
+
|
65
|
+
## Sample Result
|
66
|
+
|
67
|
+
This is a sample result running on my environement
|
68
|
+
|
69
|
+
|
70
|
+
Machine Spec
|
71
|
+
|
72
|
+
```
|
73
|
+
CPU Xeon E5-2670 2.60GHz x 2 (32 Cores)
|
74
|
+
Memory 24G
|
75
|
+
Disk 300G(10000rpm) x 2 [SAS-HDD]
|
76
|
+
OS CentOS release 6.2 (Final)
|
77
|
+
```
|
78
|
+
|
79
|
+
Result
|
80
|
+
|
81
|
+
|
82
|
+
| rate of writing (lines/sec) | reading (lines/sec) | CPU (%) | Memory (kB) | Remarks |
|
83
|
+
|-----------------------------|-----------------------|---------|-------------|---------|
|
84
|
+
| 10 | 10 | 0.2 | 29304 | |
|
85
|
+
| 100 | 100 | 0.3 | 35812 | |
|
86
|
+
| 1000 | 1000 | 1.3 | 37864 | |
|
87
|
+
| 10000 | 10000 | 6.6 | 39912 | |
|
88
|
+
| 100000 | 100000 | 62 | 39912 | |
|
89
|
+
| 200000 | 157148 | 100.4 | 36280 | MAX |
|
90
|
+
| 300000 | N/A | | | |
|
91
|
+
| 400000 | N/A | | | |
|
92
|
+
| 5247047 | N/A | | | MAX of dummer tool |
|
93
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<source>
|
2
|
+
type tail
|
3
|
+
path dummy.log
|
4
|
+
pos_file /var/tmp/_var_log_dummy.pos
|
5
|
+
format none
|
6
|
+
tag dummy
|
7
|
+
</source>
|
8
|
+
<match dummy>
|
9
|
+
type copy
|
10
|
+
<store>
|
11
|
+
type keep_forward
|
12
|
+
flush_interval 0
|
13
|
+
try_flush_interval 1
|
14
|
+
buffer_chunk_limit 1m
|
15
|
+
buffer_queue_limit 64
|
16
|
+
num_threads 3
|
17
|
+
keepforward thread
|
18
|
+
keepalive true
|
19
|
+
<server>
|
20
|
+
host 127.0.0.1 # FIX ME
|
21
|
+
port 24224
|
22
|
+
</server>
|
23
|
+
</store>
|
24
|
+
</match>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<source>
|
2
|
+
type measure_time
|
3
|
+
</source>
|
4
|
+
|
5
|
+
<source>
|
6
|
+
type forward
|
7
|
+
port 24224
|
8
|
+
<measure_time>
|
9
|
+
hook on_message
|
10
|
+
</measure_time>
|
11
|
+
</source>
|
12
|
+
<match dummy>
|
13
|
+
type flowcounter_simple
|
14
|
+
unit second
|
15
|
+
</match>
|
16
|
+
|
17
|
+
<match measure_time>
|
18
|
+
type stdout
|
19
|
+
</match>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# bundle exec fluentd -c patched_in_forward.conf -p plugin
|
2
|
+
<source>
|
3
|
+
type forward
|
4
|
+
port 24224
|
5
|
+
<elapsed>
|
6
|
+
hook on_message
|
7
|
+
</elapsed>
|
8
|
+
</source>
|
9
|
+
<match dummy>
|
10
|
+
type flowcounter_simple
|
11
|
+
unit second
|
12
|
+
</match>
|
13
|
+
|
14
|
+
<match elapsed>
|
15
|
+
type stdout
|
16
|
+
</match>
|
@@ -0,0 +1,345 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
|
20
|
+
|
21
|
+
class ForwardInput < Input
|
22
|
+
Plugin.register_input('forward', self)
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
super
|
26
|
+
require 'fluent/plugin/socket_util'
|
27
|
+
end
|
28
|
+
|
29
|
+
config_param :port, :integer, :default => DEFAULT_LISTEN_PORT
|
30
|
+
config_param :bind, :string, :default => '0.0.0.0'
|
31
|
+
config_param :backlog, :integer, :default => nil
|
32
|
+
# SO_LINGER 0 to send RST rather than FIN to avoid lots of connections sitting in TIME_WAIT at src
|
33
|
+
config_param :linger_timeout, :integer, :default => 0
|
34
|
+
attr_reader :elapsed # for test
|
35
|
+
|
36
|
+
def configure(conf)
|
37
|
+
super
|
38
|
+
|
39
|
+
if element = conf.elements.first { |element| element.name == 'elapsed' }
|
40
|
+
tag = element["tag"] || 'elapsed'
|
41
|
+
interval = element["interval"].to_i || 60
|
42
|
+
hook = element['hook'] || 'on_message'
|
43
|
+
@elapsed = ElapsedMeasure.new(log, tag, interval, hook)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def start
|
48
|
+
@loop = Coolio::Loop.new
|
49
|
+
|
50
|
+
@lsock = listen
|
51
|
+
@loop.attach(@lsock)
|
52
|
+
|
53
|
+
@usock = SocketUtil.create_udp_socket(@bind)
|
54
|
+
@usock.bind(@bind, @port)
|
55
|
+
@usock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
|
56
|
+
@hbr = HeartbeatRequestHandler.new(@usock, method(:on_heartbeat_request))
|
57
|
+
@loop.attach(@hbr)
|
58
|
+
|
59
|
+
@thread = Thread.new(&method(:run))
|
60
|
+
@elapsed.start if @elapsed
|
61
|
+
@cached_unpacker = $use_msgpack_5 ? nil : MessagePack::Unpacker.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def shutdown
|
65
|
+
@loop.watchers.each {|w| w.detach }
|
66
|
+
@loop.stop
|
67
|
+
@usock.close
|
68
|
+
listen_address = (@bind == '0.0.0.0' ? '127.0.0.1' : @bind)
|
69
|
+
# This line is for connecting listen socket to stop the event loop.
|
70
|
+
# We should use more better approach, e.g. using pipe, fixing cool.io with timeout, etc.
|
71
|
+
TCPSocket.open(listen_address, @port) {|sock| } # FIXME @thread.join blocks without this line
|
72
|
+
@thread.join
|
73
|
+
@lsock.close
|
74
|
+
@elapsed.stop if @elapsed
|
75
|
+
end
|
76
|
+
|
77
|
+
class ElapsedMeasure
|
78
|
+
attr_reader :tag, :interval, :hook, :times, :sizes, :mutex, :thread, :log
|
79
|
+
def initialize(log, tag, interval, hook)
|
80
|
+
@log = log
|
81
|
+
@tag = tag
|
82
|
+
@interval = interval
|
83
|
+
@hook = hook.split(',')
|
84
|
+
@times = []
|
85
|
+
@sizes = []
|
86
|
+
@mutex = Mutex.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def add(time, size)
|
90
|
+
@times << time
|
91
|
+
@sizes << size
|
92
|
+
end
|
93
|
+
|
94
|
+
def clear
|
95
|
+
@times.clear
|
96
|
+
@sizes.clear
|
97
|
+
end
|
98
|
+
|
99
|
+
def hookable?(caller)
|
100
|
+
@hook.include?(caller.to_s)
|
101
|
+
end
|
102
|
+
|
103
|
+
def measure_time(caller, size)
|
104
|
+
if hookable?(caller)
|
105
|
+
started = Time.now
|
106
|
+
yield
|
107
|
+
elapsed = (Time.now - started).to_f
|
108
|
+
log.debug "in_forward: elapsed time at #{caller} is #{elapsed} sec for #{size} bytes"
|
109
|
+
@mutex.synchronize { self.add(elapsed, size) }
|
110
|
+
else
|
111
|
+
yield
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def start
|
116
|
+
@thread = Thread.new(&method(:run))
|
117
|
+
end
|
118
|
+
|
119
|
+
def stop
|
120
|
+
@thread.terminate
|
121
|
+
@thread.join
|
122
|
+
end
|
123
|
+
|
124
|
+
def run
|
125
|
+
@last_checked ||= Engine.now
|
126
|
+
while (sleep 0.5)
|
127
|
+
begin
|
128
|
+
now = Engine.now
|
129
|
+
if now - @last_checked >= @interval
|
130
|
+
flush(now)
|
131
|
+
@last_checked = now
|
132
|
+
end
|
133
|
+
rescue => e
|
134
|
+
log.warn "in_forward: #{e.class} #{e.message} #{e.backtrace.first}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def flush(now)
|
140
|
+
times, sizes = [], []
|
141
|
+
@mutex.synchronize do
|
142
|
+
times = @times.dup
|
143
|
+
sizes = @sizes.dup
|
144
|
+
self.clear
|
145
|
+
end
|
146
|
+
if !times.empty? and !sizes.empty?
|
147
|
+
num = times.size
|
148
|
+
max = num == 0 ? 0 : times.max
|
149
|
+
avg = num == 0 ? 0 : times.map(&:to_f).inject(:+) / num.to_f
|
150
|
+
size_max = num == 0 ? 0 : sizes.max
|
151
|
+
size_avg = num == 0 ? 0 : sizes.map(&:to_f).inject(:+) / num.to_f
|
152
|
+
Engine.emit(@tag, now, {:num => num, :max => max, :avg => avg, :size_max => size_max, :size_avg => size_avg})
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def listen
|
158
|
+
log.info "listening fluent socket on #{@bind}:#{@port}"
|
159
|
+
s = Coolio::TCPServer.new(@bind, @port, Handler, @linger_timeout, log, method(:on_message), @elapsed)
|
160
|
+
s.listen(@backlog) unless @backlog.nil?
|
161
|
+
s
|
162
|
+
end
|
163
|
+
|
164
|
+
#config_param :path, :string, :default => DEFAULT_SOCKET_PATH
|
165
|
+
#def listen
|
166
|
+
# if File.exist?(@path)
|
167
|
+
# File.unlink(@path)
|
168
|
+
# end
|
169
|
+
# FileUtils.mkdir_p File.dirname(@path)
|
170
|
+
# log.debug "listening fluent socket on #{@path}"
|
171
|
+
# Coolio::UNIXServer.new(@path, Handler, method(:on_message))
|
172
|
+
#end
|
173
|
+
|
174
|
+
def run
|
175
|
+
@loop.run
|
176
|
+
rescue => e
|
177
|
+
log.error "unexpected error", :error => e, :error_class => e.class
|
178
|
+
log.error_backtrace
|
179
|
+
end
|
180
|
+
|
181
|
+
protected
|
182
|
+
# message Entry {
|
183
|
+
# 1: long time
|
184
|
+
# 2: object record
|
185
|
+
# }
|
186
|
+
#
|
187
|
+
# message Forward {
|
188
|
+
# 1: string tag
|
189
|
+
# 2: list<Entry> entries
|
190
|
+
# }
|
191
|
+
#
|
192
|
+
# message PackedForward {
|
193
|
+
# 1: string tag
|
194
|
+
# 2: raw entries # msgpack stream of Entry
|
195
|
+
# }
|
196
|
+
#
|
197
|
+
# message Message {
|
198
|
+
# 1: string tag
|
199
|
+
# 2: long? time
|
200
|
+
# 3: object record
|
201
|
+
# }
|
202
|
+
def on_message(msg)
|
203
|
+
if msg.nil?
|
204
|
+
# for future TCP heartbeat_request
|
205
|
+
return
|
206
|
+
end
|
207
|
+
|
208
|
+
# TODO format error
|
209
|
+
tag = msg[0].to_s
|
210
|
+
entries = msg[1]
|
211
|
+
|
212
|
+
if entries.class == String
|
213
|
+
# PackedForward
|
214
|
+
bytesize = tag.bytesize + entries.bytesize
|
215
|
+
measure_time(:on_message, bytesize) do
|
216
|
+
es = MessagePackEventStream.new(entries, @cached_unpacker)
|
217
|
+
Engine.emit_stream(tag, es)
|
218
|
+
end
|
219
|
+
|
220
|
+
elsif entries.class == Array
|
221
|
+
# Forward
|
222
|
+
es = MultiEventStream.new
|
223
|
+
entries.each {|e|
|
224
|
+
record = e[1]
|
225
|
+
next if record.nil?
|
226
|
+
time = e[0].to_i
|
227
|
+
time = (now ||= Engine.now) if time == 0
|
228
|
+
es.add(time, record)
|
229
|
+
}
|
230
|
+
measure_time(:on_message, 0) do
|
231
|
+
Engine.emit_stream(tag, es)
|
232
|
+
end
|
233
|
+
|
234
|
+
else
|
235
|
+
# Message
|
236
|
+
record = msg[2]
|
237
|
+
return if record.nil?
|
238
|
+
time = msg[1]
|
239
|
+
time = Engine.now if time == 0
|
240
|
+
bytesize = time.size + record.to_s.bytesize
|
241
|
+
measure_time(:on_message, bytesize) do
|
242
|
+
Engine.emit(tag, time, record)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def measure_time(caller, size)
|
248
|
+
@elapsed ? @elapsed.measure_time(caller, size) { yield } : yield
|
249
|
+
end
|
250
|
+
|
251
|
+
class Handler < Coolio::Socket
|
252
|
+
def initialize(io, linger_timeout, log, on_message, elapsed)
|
253
|
+
super(io)
|
254
|
+
if io.is_a?(TCPSocket)
|
255
|
+
opt = [1, linger_timeout].pack('I!I!') # { int l_onoff; int l_linger; }
|
256
|
+
io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
|
257
|
+
end
|
258
|
+
@on_message = on_message
|
259
|
+
@log = log
|
260
|
+
@log.trace {
|
261
|
+
remote_port, remote_addr = *Socket.unpack_sockaddr_in(@_io.getpeername) rescue nil
|
262
|
+
"accepted fluent socket from '#{remote_addr}:#{remote_port}': object_id=#{self.object_id}"
|
263
|
+
}
|
264
|
+
@elapsed = elapsed
|
265
|
+
end
|
266
|
+
|
267
|
+
def on_connect
|
268
|
+
end
|
269
|
+
|
270
|
+
def on_read(data)
|
271
|
+
first = data[0]
|
272
|
+
if first == '{' || first == '['
|
273
|
+
m = method(:on_read_json)
|
274
|
+
@y = Yajl::Parser.new
|
275
|
+
@y.on_parse_complete = @on_message
|
276
|
+
else
|
277
|
+
m = method(:on_read_msgpack)
|
278
|
+
@u = MessagePack::Unpacker.new
|
279
|
+
end
|
280
|
+
|
281
|
+
(class << self; self; end).module_eval do
|
282
|
+
define_method(:on_read, m)
|
283
|
+
end
|
284
|
+
m.call(data)
|
285
|
+
end
|
286
|
+
|
287
|
+
def measure_time(caller, size)
|
288
|
+
@elapsed ? @elapsed.measure_time(caller, size) { yield } : yield
|
289
|
+
end
|
290
|
+
|
291
|
+
def on_read_json(data)
|
292
|
+
measure_time(:on_read, data.bytesize) do
|
293
|
+
@y << data
|
294
|
+
end
|
295
|
+
rescue => e
|
296
|
+
@log.error "forward error", :error => e, :error_class => e.class
|
297
|
+
@log.error_backtrace
|
298
|
+
close
|
299
|
+
end
|
300
|
+
|
301
|
+
def on_read_msgpack(data)
|
302
|
+
measure_time(:on_read, data.bytesize) do
|
303
|
+
@u.feed_each(data, &@on_message)
|
304
|
+
end
|
305
|
+
rescue => e
|
306
|
+
@log.error "forward error", :error => e, :error_class => e.class
|
307
|
+
@log.error_backtrace
|
308
|
+
close
|
309
|
+
end
|
310
|
+
|
311
|
+
def on_close
|
312
|
+
@log.trace { "closed fluent socket object_id=#{self.object_id}" }
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
class HeartbeatRequestHandler < Coolio::IO
|
317
|
+
def initialize(io, callback)
|
318
|
+
super(io)
|
319
|
+
@io = io
|
320
|
+
@callback = callback
|
321
|
+
end
|
322
|
+
|
323
|
+
def on_readable
|
324
|
+
begin
|
325
|
+
msg, addr = @io.recvfrom(1024)
|
326
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR
|
327
|
+
return
|
328
|
+
end
|
329
|
+
host = addr[3]
|
330
|
+
port = addr[1]
|
331
|
+
@callback.call(host, port, msg)
|
332
|
+
rescue
|
333
|
+
# TODO log?
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def on_heartbeat_request(host, port, msg)
|
338
|
+
#log.trace "heartbeat request from #{host}:#{port}"
|
339
|
+
begin
|
340
|
+
@usock.send "\0", 0, host, port
|
341
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
@@ -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-plugin-measure_time"
|
6
|
+
gem.version = "0.1.0"
|
7
|
+
gem.authors = ["Naotoshi Seo"]
|
8
|
+
gem.email = "sonots@gmail.com"
|
9
|
+
gem.homepage = "https://github.com/sonots/fluent-plugin-measure_time"
|
10
|
+
gem.description = "Fluentd plugin 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,135 @@
|
|
1
|
+
require 'fluent/input'
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
class MeasureTimeInput < Input
|
5
|
+
Plugin.register_input('measure_time', self)
|
6
|
+
|
7
|
+
def configure(conf)
|
8
|
+
::Fluent::Input.__send__(:include, MeasureTimable)
|
9
|
+
::Fluent::Output.__send__(:include, MeasureTimable)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module MeasureTimable
|
14
|
+
def self.included(klass)
|
15
|
+
unless klass.method_defined?(:configure_without_measure_time)
|
16
|
+
klass.__send__(:alias_method, :configure_without_measure_time, :configure)
|
17
|
+
klass.__send__(:alias_method, :configure, :configure_with_measure_time)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :measure_time
|
22
|
+
|
23
|
+
def configure_with_measure_time(conf)
|
24
|
+
configure_without_measure_time(conf)
|
25
|
+
if element = conf.elements.select { |element| element.name == 'measure_time' }.first
|
26
|
+
@measure_time = MeasureTime.new(self, log)
|
27
|
+
@measure_time.configure(element)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class MeasureTime
|
33
|
+
attr_reader :plugin, :log, :times, :mutex, :thread, :tag, :interval, :hook
|
34
|
+
def initialize(plugin, log)
|
35
|
+
@plugin = plugin
|
36
|
+
@klass = @plugin.class
|
37
|
+
@log = log
|
38
|
+
@times = []
|
39
|
+
@mutex = Mutex.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def configure(conf)
|
43
|
+
@tag = conf['tag'] || 'measure_time'
|
44
|
+
unless @hook = conf['hook']
|
45
|
+
raise Fluent::ConfigError, '`hook` option must be specified in <measure_time></measure_time> directive'
|
46
|
+
end
|
47
|
+
@hook_msg = {"class" => @klass, "hook" => @hook, "object_id" => @plugin.object_id}
|
48
|
+
@interval = conf['interval'].to_i if conf['interval']
|
49
|
+
@add_or_emit_proc =
|
50
|
+
if @interval
|
51
|
+
# add to calculate statistics in each interval
|
52
|
+
Proc.new {|elapsed|
|
53
|
+
@mutex.synchronize { @times << elapsed }
|
54
|
+
}
|
55
|
+
else
|
56
|
+
# emit information immediately
|
57
|
+
Proc.new {|elapsed|
|
58
|
+
msg = {"time" => elapsed}.merge(@hook_msg)
|
59
|
+
::Fluent::Engine.emit(@tag, ::Fluent::Engine.now, msg)
|
60
|
+
}
|
61
|
+
end
|
62
|
+
apply_hook
|
63
|
+
end
|
64
|
+
|
65
|
+
def apply_hook
|
66
|
+
@plugin.instance_eval <<EOF
|
67
|
+
def #{@hook}(*args)
|
68
|
+
measure_time.measure_time do
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
def start
|
73
|
+
super
|
74
|
+
measure_time.start
|
75
|
+
end
|
76
|
+
def stop
|
77
|
+
super
|
78
|
+
measure_time.stop
|
79
|
+
end
|
80
|
+
EOF
|
81
|
+
end
|
82
|
+
|
83
|
+
def measure_time
|
84
|
+
started = Time.now
|
85
|
+
output = yield
|
86
|
+
elapsed = (Time.now - started).to_f
|
87
|
+
log.debug "elapsed time at #{@klass}##{@hook} is #{elapsed} sec"
|
88
|
+
@add_or_emit_proc.call(elapsed)
|
89
|
+
output
|
90
|
+
end
|
91
|
+
|
92
|
+
def start
|
93
|
+
return unless @interval
|
94
|
+
@thread = Thread.new(&method(:run))
|
95
|
+
end
|
96
|
+
|
97
|
+
def stop
|
98
|
+
return unless @interval
|
99
|
+
@thread.terminate
|
100
|
+
@thread.join
|
101
|
+
end
|
102
|
+
|
103
|
+
def run
|
104
|
+
@last_checked ||= Engine.now
|
105
|
+
while (sleep 0.5)
|
106
|
+
begin
|
107
|
+
now = Engine.now
|
108
|
+
if now - @last_checked >= @interval
|
109
|
+
flush(now)
|
110
|
+
@last_checked = now
|
111
|
+
end
|
112
|
+
rescue => e
|
113
|
+
log.warn "in_measure_time: hook #{@klass}##{@hook} #{e.class} #{e.message} #{e.backtrace.first}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def flush(now)
|
119
|
+
times = []
|
120
|
+
@mutex.synchronize do
|
121
|
+
times = @times.dup
|
122
|
+
@times.clear
|
123
|
+
end
|
124
|
+
triple = nil
|
125
|
+
unless times.empty?
|
126
|
+
num = times.size
|
127
|
+
max = num == 0 ? 0 : times.max
|
128
|
+
avg = num == 0 ? 0 : times.map(&:to_f).inject(:+) / num.to_f
|
129
|
+
triple = [@tag, now, {:max => max, :avg => avg, :num => num}.merge(@hook_msg)]
|
130
|
+
Engine.emit(*triple)
|
131
|
+
end
|
132
|
+
triple
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,129 @@
|
|
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::MeasureTimeInput do
|
7
|
+
before { Fluent::Test.setup }
|
8
|
+
|
9
|
+
def create_driver(conf=%[])
|
10
|
+
Fluent::Test::InputTestDriver.new(Fluent::MeasureTimeInput).configure(conf)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'test configure' do
|
14
|
+
it { expect { create_driver }.not_to raise_error }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "extends Fluent::ForwardInput" do
|
19
|
+
before { Fluent::Test.setup }
|
20
|
+
|
21
|
+
def create_driver(conf=CONFIG)
|
22
|
+
Fluent::Test::InputTestDriver.new(Fluent::ForwardInput).configure(conf)
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect
|
26
|
+
TCPSocket.new('127.0.0.1', PORT)
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_data(data)
|
30
|
+
io = connect
|
31
|
+
begin
|
32
|
+
io.write data
|
33
|
+
ensure
|
34
|
+
io.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.unused_port
|
39
|
+
s = TCPServer.open(0)
|
40
|
+
port = s.addr[1]
|
41
|
+
s.close
|
42
|
+
port
|
43
|
+
end
|
44
|
+
|
45
|
+
PORT = unused_port
|
46
|
+
CONFIG = %[
|
47
|
+
port #{PORT}
|
48
|
+
bind 127.0.0.1
|
49
|
+
]
|
50
|
+
|
51
|
+
let(:driver) { create_driver(config) }
|
52
|
+
|
53
|
+
describe 'test configure' do
|
54
|
+
let(:config) {CONFIG + %[
|
55
|
+
<measure>
|
56
|
+
tag test
|
57
|
+
interval 10
|
58
|
+
hook on_message
|
59
|
+
</measure>
|
60
|
+
]}
|
61
|
+
let(:subject) { driver.instance.measure }
|
62
|
+
its(:tag) { should == 'test' }
|
63
|
+
its(:interval) { should == 10 }
|
64
|
+
its(:hook) { should == 'on_message' }
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'test emit' do
|
68
|
+
let(:config) {CONFIG + %[
|
69
|
+
<measure>
|
70
|
+
tag measure
|
71
|
+
interval 1
|
72
|
+
# hook Fluent::ForwardInput::Handler.on_read # not support inner class yet
|
73
|
+
hook Fluent::ForwardInput.on_message
|
74
|
+
</measure>
|
75
|
+
]}
|
76
|
+
it 'should flush' do
|
77
|
+
d = driver.instance
|
78
|
+
d.__send__(:on_message, ['tag1', 0, {'a'=>1}].to_msgpack)
|
79
|
+
triple = d.measure.flush(0)
|
80
|
+
triple[0].should == 'measure'
|
81
|
+
triple[2].keys.should =~ [:num, :max, :avg]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "extends Fluent::StdoutOutput" do
|
87
|
+
before { Fluent::Test.setup }
|
88
|
+
|
89
|
+
def create_driver(conf=CONFIG, tag = 'test')
|
90
|
+
Fluent::Test::OutputTestDriver.new(Fluent::StdoutOutput, tag).configure(conf)
|
91
|
+
end
|
92
|
+
|
93
|
+
CONFIG = %[
|
94
|
+
]
|
95
|
+
|
96
|
+
let(:driver) { create_driver(config) }
|
97
|
+
|
98
|
+
describe 'test configure' do
|
99
|
+
let(:config) {CONFIG + %[
|
100
|
+
<measure>
|
101
|
+
tag test
|
102
|
+
interval 10
|
103
|
+
hook emit
|
104
|
+
</measure>
|
105
|
+
]}
|
106
|
+
let(:subject) { driver.instance.instance_variable_get(:@measure) }
|
107
|
+
its(:tag) { should == 'test' }
|
108
|
+
its(:interval) { should == 10 }
|
109
|
+
its(:hook) { should == 'emit' }
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'test emit' do
|
113
|
+
let(:config) {CONFIG + %[
|
114
|
+
<measure>
|
115
|
+
tag measure
|
116
|
+
interval 1
|
117
|
+
hook emit
|
118
|
+
</measure>
|
119
|
+
]}
|
120
|
+
it 'should flush' do
|
121
|
+
d = driver.instance
|
122
|
+
d.emit('tag1', Fluent::OneEventStream.new(0, {'a'=>1}), Fluent::NullOutputChain.instance)
|
123
|
+
triple = d.measure.flush(0)
|
124
|
+
triple[0].should == 'measure'
|
125
|
+
triple[2].keys.should =~ [:num, :max, :avg]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -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/plugin/in_measure_time'
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-measure_time
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Naotoshi Seo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-12 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 plugin 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
|
+
- benchmark/Gemfile
|
98
|
+
- benchmark/README.md
|
99
|
+
- benchmark/agent.conf
|
100
|
+
- benchmark/dummer.conf
|
101
|
+
- benchmark/hooked_in_forward.conf
|
102
|
+
- benchmark/in_forward.conf
|
103
|
+
- benchmark/patched_in_forward.conf
|
104
|
+
- benchmark/plugin/in_forward.rb
|
105
|
+
- fluent-plugin-measure_time.gemspec
|
106
|
+
- lib/fluent/plugin/in_measure_time.rb
|
107
|
+
- spec/in_measure_time_spec.rb
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
homepage: https://github.com/sonots/fluent-plugin-measure_time
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
|
+
metadata: {}
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 2.0.3
|
130
|
+
signing_key:
|
131
|
+
specification_version: 4
|
132
|
+
summary: Fluentd plugin to measure elapsed time to process messages
|
133
|
+
test_files:
|
134
|
+
- spec/in_measure_time_spec.rb
|
135
|
+
- spec/spec_helper.rb
|