fluent-plugin-statsite 0.0.4 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18bf875092d09bd490fd29b12b3749aeefc44248
4
- data.tar.gz: 522741407db13e270fbd60fa523020d0428ecb5a
3
+ metadata.gz: 19a955bd7d1edfb5cc432376589593364d276a3a
4
+ data.tar.gz: 67c75225376f3a128618ff3dfbc7fd584f5d1ed0
5
5
  SHA512:
6
- metadata.gz: 0f48dfb35f0409afa99d40aa18bc8599b98bafa13d78fdc2a177aa2404fd8bd173ee92380c08bc1e73015563fec80535768a69ecd9f7fb6ddec6a9aa0b690399
7
- data.tar.gz: e61528aee4cd10d6ad2821d1cdbe5ecd48d4ef02c97b0f04fa7f3dbf87addeef6528c149ecba9897236f9d7edd5e642001b0080dc464b8e8a1f59b06547959d4
6
+ metadata.gz: 4b0f6e5e0c20122d87141bd21e9a3e2bf7cc326399de0b2a648a0c72866bea9a62e34ca1d5870e70e8c0890719424305020faea7312b3c0ba15b613e78fcc8e1
7
+ data.tar.gz: b18f846e67dbf88bc580ee5ef9a902d6ce04a24754a31c13a094aa4ab6b181c0270bc6f4c6534585a9c9ce0ed8d19dc2ec0372654f6cf7a09d8270dd04914284
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Statsite Fluentd Plugin
2
2
 
3
+ [![Build Status](https://travis-ci.org/choplin/fluent-plugin-statsite.svg?branch=master)](https://travis-ci.org/choplin/fluent-plugin-statsite)
4
+
3
5
  This plugin calculates various useful metrics using [Statsite by armon](http://armon.github.io/statsite/).
4
6
 
5
7
  [Statsite](http://armon.github.io/statsite/) is very cool software. Statsite works as daemon service, receiving events from tcp/udp, aggregating these events with specified methods, and sending the results via pluggable sinks. Statsite is written in C, cpu and memory efficient, and employ some approximate algorithms for unique sets and percentiles.
@@ -8,6 +10,67 @@ This plugin calculates various useful metrics using [Statsite by armon](http://a
8
10
 
9
11
  Statsite process is launched as a child process from this plugin internally. All you have to do place statsite the binary under $PATH, or set the path of statsite binary as parameter. Neither config files or daemon process is not required. Besides, the communication between the plugin and the Statsite process takes place through STDIN/STDOUT, so no network port will be used.
10
12
 
13
+ ## Quickstart
14
+
15
+ Assume that nginx log events like below come.
16
+
17
+ ```json
18
+ {
19
+ "remote_addr":"114.170.6.118",
20
+ "remote_user":"-",
21
+ "time_local":"20/Jul/2014:18:25:50 +0000",
22
+ "request":"GET /foo HTTP/1.1",
23
+ "status":"200",
24
+ "body_bytes_sent":"911",
25
+ "http_referer":"-",
26
+ "http_user_agent":"Mozilla/5.0 (Linux; U; Android 4.2.2; ja-jp; SO-04E Build/10.3.1.B.0.256) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
27
+ "request_time":"0.058",
28
+ "upstream_addr":"192.168.222.180:80",
29
+ "upstream_response_time":"0.058"
30
+ }
31
+ ```
32
+
33
+ and you set a fluentd config as,
34
+
35
+ ```
36
+ <match **>
37
+ type statsite_filter
38
+ tag statsite
39
+ metrics [
40
+ "status_${status}:1|c",
41
+ "request_time|${request_time}|ms"}
42
+ ]
43
+ histograms [
44
+ {"prefix": "request_time", "min": 0, "max": 1, "width": 0.1}
45
+ ]
46
+ statsite_flush_interval 1s
47
+ flush_interval 1s
48
+ </match>
49
+ ```
50
+
51
+ then you will get events such as below every specified seconds.
52
+
53
+ ```json
54
+ statsite 1406124737 {"type":"counts","key":"status_500","value":1.0}
55
+ statsite 1406124737 {"type":"counts","key":"status_200","value":3.0}
56
+ statsite 1406124737 {"type":"counts","key":"status_302","value":1.0}
57
+ statsite 1406124737 {"type":"timers","key":"request_time","value":12.0,"statistic":"sum"}
58
+ statsite 1406124737 {"type":"timers","key":"request_time","value":40.0,"statistic":"sum_sq"}
59
+ statsite 1406124737 {"type":"timers","key":"request_time","value":2.4,"statistic":"mean"}
60
+ statsite 1406124737 {"type":"timers","key":"request_time","value":1.0,"statistic":"lower"}
61
+ statsite 1406124737 {"type":"timers","key":"request_time","value":5.0,"statistic":"upper"}
62
+ statsite 1406124737 {"type":"timers","key":"request_time","value":5,"statistic":"count"}
63
+ statsite 1406124737 {"type":"timers","key":"request_time","value":1.67332,"statistic":"stdev"}
64
+ statsite 1406124737 {"type":"timers","key":"request_time","value":2.0,"statistic":"median"}
65
+ statsite 1406124737 {"type":"timers","key":"request_time","value":5.0,"statistic":"p95"}
66
+ statsite 1406124737 {"type":"timers","key":"request_time","value":5.0,"statistic":"p99"}
67
+ statsite 1406124737 {"type":"timers","key":"request_time","value":12.0,"statistic":"rate"}
68
+ ```
69
+
70
+ ## Prerequisite
71
+
72
+ You have to install Statsite on the machine where fluentd is running.
73
+
11
74
  ## Installation
12
75
 
13
76
  `$ fluent-gem install fluent-plugin-statsite`
@@ -20,44 +83,167 @@ Please refer to [Statsite official page](http://armon.github.io/statsite/).
20
83
 
21
84
  ## Configuration
22
85
 
23
- It is strongly recommended to use '[V1 config format](http://docs.fluentd.org/articles/config-file#v1-format)' because this plugin requires to set deeply nested parameters.
86
+ It is strongly recommended to use '[V1 config format](http://docs.fluentd.org/articles/config-file#v1-format)' because this plugin requires to set deeply nested parameters.
87
+
88
+ ### Parameter
89
+
90
+ key | type | description | required | default
91
+ --- | --- | --- | --- | ---
92
+ tag | string | The tag of output events. | yes |
93
+ metrics | array | How to retrive statsite messages from fluentd event. see the details below. | yes |
94
+ histograms | array | THe statstie histogram settings. see the details below. | no | []
95
+ statiste_path | string | The path of statsite command. Leave this blank if statsite places under $PATH. | yes | statsite
96
+ statsite_flush_interval | time | The interval at which statsite flush aggregated results. | no | 10s
97
+ stream_cmd | string | This is the command that statsite invokes every flush_interval seconds to handle the metrics. It can be any executable. It should read inputs over stdin and exit with status code 0 on success. | no | cat
98
+ time_eps | float | The upper bound on error for timer estimates. Please refer to statsite official page. | no | 0.01
99
+ set_eps | float | The upper bound on error for unique set estimates. Please refer to statsite official page. | no | 0.02
100
+ child_respawn | string | How many times statsite will be respawned in case of unexpected exit. | no |
101
+
102
+ ### Metrics
103
+
104
+ Metrics parameter specifies how to form messages to send to statsite from each fluentd event.
24
105
 
25
- ### Example
106
+ Top level of **metrics* parameter must be an array which contains strings or hashes. If you set multliple elements in an array, equivalent number of messages will be sent to statsite for one fluentd event.
107
+
108
+ For example, given this metrics setting,
26
109
 
27
110
  ```
28
- <match **>
29
- type statsite_filter
30
- tag statsite
31
- metrics [
32
- "${status}:1|c",
33
- {"key": "request_time", "value": "request_time", "type": "ms"}
34
- ]
35
- histograms [
36
- {"prefix": "request_time", "min": 0, "max": 1, "width": 0.1}
37
- ]
38
- statsite_path "statsite"
39
- statsite_flush_interval 1s
40
- timer_eps 0.01
41
- set_eps 0.01
42
- child_respawn 5
43
- </match>
111
+ metrics [
112
+ {"key": "key_1", "value": "1", "type": "s"}
113
+ {"key": "key_2", "value": "1", "type": "s"}
114
+ ]
44
115
  ```
45
116
 
46
- ### Parameter
117
+ and this fluentd event is comming,
118
+
119
+ ```
120
+ {"foo": "f", "bar": "b", "hoge": "h"}
121
+ ```
122
+
123
+ then the events below will be sent to statsite
124
+
125
+ ```
126
+ key_1:1|c
127
+ key_2:1|c
128
+ ```
129
+
130
+ Every element of the array must foloow the one of these format, the string format, and the hash format. Both they have the same fields semantically. See the details of these formats below.
47
131
 
48
- TODO
132
+ #### Fields
49
133
 
50
- ### Metrics Format
134
+ key | type | description | required
135
+ --- | --- | --- | ---
136
+ key | string | message key | yes
137
+ value_time | string | message value | yes
138
+ type | enum | statsite message type. | yes
51
139
 
52
- You can specify metrics in two format, string style, and hash style.
140
+ With these settings, A message sent to statsite is "${key}:${value}|${type}\n"
53
141
 
54
- #### String style
142
+ You should also see [Statsite official page](http://armon.github.io/statsite/) to see the statsite supports in detail.
55
143
 
56
- TODO
144
+ #### String format
145
+
146
+ String format almost the same as Statsite's event protocol, though this format supports variable substitution in key and value field, which is described in detail below.
147
+
148
+ ##### Example
149
+
150
+ ```
151
+ metrics [
152
+ "status_${status}:1|c"
153
+ ]
154
+ ```
155
+
156
+ #### Hash format
157
+
158
+ Hash format is expressed as JSON Object.
159
+
160
+ ##### Example
161
+
162
+ ```json
163
+ metrics [
164
+ {"key": "status_${status}", "value": "1", "type": "c"}
165
+ ]
166
+ ```
167
+
168
+ #### Message Type
169
+
170
+ Please refer to [Statsite official page](http://armon.github.io/statsite/).
171
+
172
+ type | description
173
+ --- | ---
174
+ kv | Simple Key/Value.
175
+ g | Gauge, similar to kv but only the last value per key is retained
176
+ ms | Timer.
177
+ h | Alias for timer
178
+ c | Counter.
179
+ s | Unique Set
180
+
181
+ #### Variable substitution
182
+
183
+ Both in string and hash format, key and value field support substitution of variable.
184
+
185
+ When the string "${*subst_key*}" exists key or value field, this will be replaces with the corresponding value in the fluentd event.
186
+
187
+ For example, given this metrics setting,
188
+
189
+ ```
190
+ metrics [
191
+ {"key": "key_${foo}_${bar}", "value": "${hoge}", "type": "c"}
192
+ ]
193
+ ```
194
+
195
+ and this fluentd event is comming,
196
+
197
+ ```
198
+ {"foo": "f", "bar": "b", "hoge": "1"}
199
+ ```
200
+
201
+ then the events below will be sent to statsite
202
+
203
+ ```
204
+ key_f_b:1|s
205
+ ```
206
+
207
+ When any one of the substitution key does not exists in the fluentd event, no messages will be sent to statsite for that metric element.
208
+
209
+ For example, given this metrics setting,
210
+
211
+ ```
212
+ metrics [
213
+ {"key": "key_${foo}", "value": "1", "type": "c"},
214
+ {"key": "key_${bar}", "value": "1", "type": "c"}
215
+ ]
216
+ ```
217
+
218
+ and this fluentd event is comming,
219
+
220
+ ```
221
+ {"foo": "f"}
222
+
223
+ then the events below will be sent to statsite
224
+
225
+ ```
226
+ key_f:1|c
227
+ ```
228
+
229
+ ### Histograms
230
+
231
+ #### Example
232
+
233
+ ```json
234
+ histogram [
235
+ {"prefix": "request_time", "min": 0, "max": 1, "width": 0.1}
236
+ ]
237
+ ```
57
238
 
58
- #### Hash style
239
+ #### Fields
59
240
 
60
- TODO
241
+ key | type | description | required
242
+ --- | --- | --- | ---
243
+ prefix | string | This is the key prefix to match on. This is also used as a suffix of section name. | no
244
+ min | float | The minimum bound on the histogram. | yes
245
+ max | float | The maximum bound on the histogram. | yes
246
+ width | float | The width of each bucket between the min and max. | yes
61
247
 
62
248
  ## Copyright
63
249
 
@@ -4,13 +4,12 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "fluent-plugin-statsite"
7
- spec.version = "0.0.4"
7
+ spec.version = "0.0.7"
8
8
  spec.authors = ["OKUNO Akihiro"]
9
9
  spec.email = ["choplin.choplin@gmail.com"]
10
10
  spec.summary = %q{Fluentd statsite plugin}
11
11
  spec.description = %q{Fluentd plugin which caluculate statistics using statsite}
12
- spec.homepage = ""
13
- spec.homepage = "https://github.com/choplin/fluent-plugin-statsite"
12
+ spec.homepage = "https://github.com/choplin/fluent-plugin-statsite"
14
13
  spec.license = "Apache-2.0"
15
14
 
16
15
  spec.files = `git ls-files -z`.split("\x0")
@@ -1,5 +1,6 @@
1
1
  require_relative 'statsite/child_process'
2
- require_relative 'statsite/format'
2
+ require_relative 'statsite/formatter'
3
+ require_relative 'statsite/parser'
3
4
  require_relative 'statsite/metric'
4
5
  require_relative 'statsite/histogram'
5
6
 
@@ -20,6 +21,7 @@ module Fluent
20
21
  config_param :histograms, :array, :default => []
21
22
  config_param :statsite_path, :string, :default => 'statsite'
22
23
  config_param :statsite_flush_interval, :time, :default => 10
24
+ config_param :stream_cmd, :string, :default => 'cat'
23
25
  config_param :timer_eps, :float, :default => 0.01
24
26
  config_param :set_eps, :float, :default => 0.02
25
27
  config_param :child_respawn, :string, :default => nil
@@ -143,7 +145,7 @@ log_level = INFO
143
145
  flush_interval = #{@statsite_flush_interval}
144
146
  timer_eps = #{@timer_eps}
145
147
  set_eps = #{@set_eps}
146
- stream_cmd = cat
148
+ stream_cmd = #{@stream_cmd}
147
149
 
148
150
  #{@histograms.map(&:to_ini).join("\n\n")}
149
151
  CONFIG
@@ -0,0 +1,13 @@
1
+ module Fluent
2
+ module StatsitePlugin
3
+ class StatsiteFormatter
4
+ def initialize(metrics)
5
+ @metrics = metrics
6
+ end
7
+
8
+ def call(record)
9
+ @metrics.map{|m| m.convert(record)}.select{|m| not m.nil?}.join('')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,91 @@
1
+ module Fluent
2
+ module StatsitePlugin
3
+ module Parser
4
+ def parse_line(line)
5
+ k,v,t = line.chomp.split('|')
6
+ record = build_record(k,v)
7
+ [t.to_i, record]
8
+ end
9
+
10
+ def build_record(k,v)
11
+ type, key, statistic, range = k.split(".", 4)
12
+
13
+ case type
14
+ when 'timers' then 1
15
+ if statistic == 'histogram'
16
+ {type: type, key: key, value: v.to_i, statistic: statistic, range: range[4..-1]}
17
+ elsif statistic == 'count'
18
+ {type: type, key: key, value: v.to_i, statistic: statistic}
19
+ else
20
+ {type: type, key: key, value: v.to_f, statistic: statistic}
21
+ end
22
+ when 'kv', 'gauges', 'counts'
23
+ {type: type, key: key, value: v.to_f}
24
+ when 'sets'
25
+ {type: type, key: key, value: v.to_i}
26
+ end
27
+ end
28
+ end
29
+
30
+ class StatsiteParser
31
+ include Parser
32
+
33
+ def initialize(on_message)
34
+ @on_message = on_message
35
+ end
36
+
37
+ def call(io)
38
+ io.each_line(&method(:each_line))
39
+ end
40
+
41
+ def each_line(line)
42
+ time, record = parse_line(line)
43
+ raise "out_statsite: failed to parse a line. '#{line}'" if record.nil?
44
+
45
+ @on_message.call(time, record)
46
+ end
47
+ end
48
+
49
+ class StatsiteAggregateParser
50
+ # TODO: should be configurable?
51
+ FLUSH_WAIT = 0.5
52
+
53
+ def initialize(on_message, coolio_loop)
54
+ @on_message = on_message
55
+ @loop = coolio_loop
56
+ @buf = {}
57
+ end
58
+
59
+ def call(io)
60
+ io.each_line(&method(:each_line))
61
+ end
62
+
63
+ def each_line(line)
64
+ record = parse_line(line)
65
+
66
+ raise "out_statsite: failed to parse a line. '#{line}'" if record.nil?
67
+
68
+ timer = TimerWatcher(FLUSH_WAIT, $log, method(&:flush))
69
+ end
70
+
71
+ def flush
72
+ @on_message.call(t.to_i, record)
73
+ end
74
+
75
+ class TimerWatcher < Coolio::TimerWatcher
76
+ def initialize(duration, log, callback)
77
+ @callback = callback
78
+ @log = log
79
+ super(interval, repeat)
80
+ end
81
+
82
+ def on_timer
83
+ @callback.call
84
+ rescue
85
+ @log.error $!.to_s
86
+ @log.error_backtrace
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,5 +1,5 @@
1
1
  require 'helper'
2
- require 'fluent/plugin/statsite/format'
2
+ require 'fluent/plugin/statsite/formatter'
3
3
 
4
4
  include Fluent::StatsitePlugin
5
5
 
@@ -1,5 +1,5 @@
1
1
  require 'helper'
2
- require 'fluent/plugin/statsite/format'
2
+ require 'fluent/plugin/statsite/parser'
3
3
 
4
4
  include Fluent::StatsitePlugin
5
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-statsite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKUNO Akihiro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-21 00:00:00.000000000 Z
11
+ date: 2014-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -70,10 +70,11 @@ files:
70
70
  - fluent-plugin-statsite.gemspec
71
71
  - lib/fluent/plugin/out_statsite_filter.rb
72
72
  - lib/fluent/plugin/statsite/child_process.rb
73
- - lib/fluent/plugin/statsite/format.rb
73
+ - lib/fluent/plugin/statsite/formatter.rb
74
74
  - lib/fluent/plugin/statsite/histogram.rb
75
75
  - lib/fluent/plugin/statsite/metric.rb
76
76
  - lib/fluent/plugin/statsite/metric_format.rb
77
+ - lib/fluent/plugin/statsite/parser.rb
77
78
  - test/helper.rb
78
79
  - test/test_formatter.rb
79
80
  - test/test_histogram.rb
@@ -1,47 +0,0 @@
1
- module Fluent
2
- module StatsitePlugin
3
- class StatsiteParser
4
- def initialize(on_message)
5
- @on_message = on_message
6
- end
7
-
8
- def call(io)
9
- io.each_line(&method(:each_line))
10
- end
11
-
12
- def each_line(line)
13
- k,v,t = line.chomp.split('|')
14
- type, key, statistic, range = k.split(".", 4)
15
-
16
- record = case type
17
- when 'timers' then 1
18
- if statistic == 'histogram'
19
- {type: type, key: key, value: v.to_i, statistic: statistic, range: range[4..-1]}
20
- elsif statistic == 'count'
21
- {type: type, key: key, value: v.to_i, statistic: statistic}
22
- else
23
- {type: type, key: key, value: v.to_f, statistic: statistic}
24
- end
25
- when 'kv', 'gauges', 'counts'
26
- {type: type, key: key, value: v.to_f}
27
- when 'sets'
28
- {type: type, key: key, value: v.to_i}
29
- end
30
-
31
- raise "out_statsite: failed to parse a line. '#{line}'" if record.nil?
32
-
33
- @on_message.call(t.to_i, record)
34
- end
35
- end
36
-
37
- class StatsiteFormatter
38
- def initialize(metrics)
39
- @metrics = metrics
40
- end
41
-
42
- def call(record)
43
- @metrics.map{|m| m.convert(record)}.select{|m| not m.nil?}.join('')
44
- end
45
- end
46
- end
47
- end