fluent-plugin-http-record-modifier 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2ea15085273d646c9d75d934aa5b2e90eeef89af
4
+ data.tar.gz: 693781df064fceb076c7d99a8718a89724898aa8
5
+ SHA512:
6
+ metadata.gz: d938f24e8a733b6a9161f94e09d066d2eed540d29a5487684c14e226867a92f8fb6f7fa229ab49a09d25a89d178c2379245c65d77ad4e2ecd251d651e4a40122
7
+ data.tar.gz: 859c43d550ef56cc379bf899c6fbf7e43964103d4369fe85d9eb70e2c87b4e245eb50f8edb9ce0bec993b31600968a06fb13cfcbd66e04f6910ecdeba79cebac
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ Gemfile.lock
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
17
+ *~
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-mqtt.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Augusto Nishi
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,54 @@
1
+ # Fluent::Plugin::Simple Value to Hash
2
+
3
+ Fluent plugin for converting simple value variables to hash to be usable to others plugins
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'fluent-plugin-http-record-modifier'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install fluent-plugin-http-record-modifier
18
+
19
+ ## Usage
20
+
21
+ ```
22
+
23
+ <filter tag>
24
+ type http_record_modifier
25
+ method (defaults to GET)
26
+ renew_record (defaults to false)
27
+ endpoint_url yourapi.com/api/something/
28
+ serializer (form or json, just used on POST)
29
+ authentication (none or basic, defaults to none)
30
+ username (default to '')
31
+ password (default to '')
32
+ <params>
33
+ var ${tag_parts[1]}
34
+ var2 ${record_attr}
35
+ </params>
36
+ <record>
37
+ name ${body.name}
38
+ body ${body}
39
+ array ${body.array}
40
+ second ${body.array[1]}
41
+ last ${body.array[-1]}
42
+ deep ${body.array[0].attr}
43
+ </record>
44
+ </filter>
45
+
46
+ ```
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it ( http://github.com/DEVTecnologia/fluent-plugin-http-record-modifier/fork )
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fluent-plugin-http-record-modifier"
7
+ spec.version = "0.0.0"
8
+ spec.authors = ["Augusto Nishi"]
9
+ spec.email = ["augusto.nishi@gmail.com"]
10
+ spec.summary = %q{fluentd filter plugin for modifing record based on a HTTP request}
11
+ spec.description = %q{fluentd filter plugin for modifing record based on a HTTP request}
12
+ spec.homepage = "http://github.com/DEVTecnologia/fluent-plugin-http-record-modifier"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "fluentd"
23
+
24
+ end
@@ -0,0 +1,288 @@
1
+ module Fluent
2
+ class HttpRecordModifier < Filter
3
+ Plugin.register_filter('http_record_modifier', self)
4
+
5
+ def initialize
6
+ require 'socket'
7
+ require 'yajl'
8
+ require 'net/http'
9
+ require 'uri'
10
+ super
11
+ end
12
+
13
+ #Based on Filter Record Tranformer that is built-in on Fluent
14
+ config_param :remove_keys, :string, :default => nil
15
+ config_param :keep_keys, :string, :default => nil
16
+ config_param :method, :string, :default => :get
17
+ config_param :rene_record, :bool, :default => false
18
+ config_param :auto_typecast, :bool, :default => false # false for lower version compatibility
19
+ config_param :endpoint_url, :string
20
+ config_param :serializer, :string, :default => :form
21
+ config_param :authentication, :string, :default => nil
22
+ config_param :username, :string, :default => ''
23
+ config_param :password, :string, :default => ''
24
+ config_param :raise_on_error, :bool, :default => true
25
+
26
+ def configure(conf)
27
+ super
28
+ @method ||= conf['method']
29
+ @map = {}
30
+ # <record></record> directive
31
+ conf.elements.select { |element| element.name == 'record' }.each do |element|
32
+ element.each_pair do |k, v|
33
+ element.has_key?(k) # to suppress unread configuration warning
34
+ @map[k] = parse_value(v)
35
+ end
36
+ end
37
+
38
+ @maped_params = {}
39
+ # <params></params> directive
40
+ conf.elements.select { |element| element.name == 'params' }.each do |element|
41
+ element.each_pair do |k, v|
42
+ element.has_key?(k) # to suppress unread configuration warning
43
+ @maped_params[k] = parse_value(v)
44
+ end
45
+ end
46
+
47
+ if @remove_keys
48
+ @remove_keys = @remove_keys.split(',')
49
+ end
50
+
51
+ if @keep_keys
52
+ raise Fluent::ConfigError, "`renew_record` must be true to use `keep_keys`" unless @renew_record
53
+ @keep_keys = @keep_keys.split(',')
54
+ end
55
+
56
+ @placeholder_expander = PlaceholderExpander.new({
57
+ :log => log,
58
+ :auto_typecast => @auto_typecast,
59
+ })
60
+
61
+ @hostname = Socket.gethostname
62
+ end
63
+
64
+ def filter_stream(tag, es)
65
+ new_es = MultiEventStream.new
66
+ tag_parts = tag.split('.')
67
+ tag_prefix = tag_prefix(tag_parts)
68
+ tag_suffix = tag_suffix(tag_parts)
69
+ placeholders = {
70
+ 'tag' => tag,
71
+ 'tag_parts' => tag_parts,
72
+ 'tag_prefix' => tag_prefix,
73
+ 'tag_suffix' => tag_suffix,
74
+ 'hostname' => @hostname,
75
+ }
76
+ last_record = nil
77
+ es.each do |time, record|
78
+ last_record = record # for debug log
79
+ req, uri = create_request(tag, time, record)
80
+ res = send_request(req, uri)
81
+ body = deserialize_body(res)
82
+ new_record = reform(time, record, placeholders, body)
83
+ if @renew_time_key && new_record.has_key?(@renew_time_key)
84
+ time = new_record[@renew_time_key].to_i
85
+ end
86
+ new_es.add(time, new_record)
87
+ end
88
+ new_es
89
+ rescue => e
90
+ log.warn "failed to reform records", :error_class => e.class, :error => e.message
91
+ log.warn_backtrace
92
+ log.debug "map:#{@map} record:#{last_record} placeholders:#{placeholders}"
93
+ end
94
+
95
+ def deserialize_body(res)
96
+ clean = {}
97
+ body = res.body
98
+ if res.content_type == 'application/json'
99
+ body = Yajl.load(body)
100
+ if body.is_a? Hash
101
+ clean = Yajl.load(res.body)
102
+ end
103
+ end
104
+ clean['body'] = body
105
+ clean
106
+ end
107
+
108
+ def format_params(tag, time, record)
109
+ tag_parts = tag.split('.')
110
+ tag_prefix = tag_prefix(tag_parts)
111
+ tag_suffix = tag_suffix(tag_parts)
112
+ placeholders = {
113
+ 'tag' => tag,
114
+ 'tag_parts' => tag_parts,
115
+ 'tag_prefix' => tag_prefix,
116
+ 'tag_suffix' => tag_suffix,
117
+ 'hostname' => @hostname,
118
+ }
119
+ @placeholder_expander.prepare_placeholders(time, record, placeholders)
120
+
121
+ expand_placeholders(@maped_params)
122
+ end
123
+
124
+ def set_body(req, tag, time, record)
125
+ if @serializer == :json
126
+ req['Content-Type'] = 'application/json'
127
+ else
128
+ req.set_form_data(record)
129
+ end
130
+ req
131
+ end
132
+
133
+
134
+ def create_request(tag, time, record)
135
+ url = URI.encode(@endpoint_url.to_s)
136
+ uri = URI.parse(url)
137
+ params = format_params(tag, time, record)
138
+ uri.query = URI.encode_www_form(params)
139
+ req = Net::HTTP.const_get(@method.to_s.capitalize).new(uri)
140
+ unless @method.to_s.capitalize == 'Get'
141
+ set_body(req, tag, time, record)
142
+ end
143
+ return req, uri
144
+ end
145
+
146
+ def send_request(req, uri)
147
+ res = nil
148
+
149
+ begin
150
+ if @auth and @auth == :basic
151
+ req.basic_auth(@username, @password)
152
+ end
153
+ res = Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req) }
154
+ rescue => e # rescue all StandardErrors
155
+ # server didn't respond
156
+ $log.warn "Net::HTTP.#{req.method.capitalize} raises exception: #{e.class}, '#{e.message}'"
157
+ raise e if @raise_on_error
158
+ end # end begin
159
+ end # end send_request
160
+
161
+ private
162
+
163
+ def parse_value(value_str)
164
+ if value_str.start_with?('{', '[')
165
+ JSON.parse(value_str)
166
+ else
167
+ value_str
168
+ end
169
+ rescue => e
170
+ log.warn "failed to parse #{value_str} as json. Assuming #{value_str} is a string", :error_class => e.class, :error => e.message
171
+ value_str # emit as string
172
+ end
173
+
174
+ def reform(time, record, opts, res)
175
+ @placeholder_expander.prepare_placeholders(time, res, opts)
176
+
177
+ new_record = @renew_record ? {} : record.dup
178
+ @keep_keys.each {|k| new_record[k] = record[k]} if @keep_keys and @renew_record
179
+ new_record.merge!(expand_placeholders(@map))
180
+ @remove_keys.each {|k| new_record.delete(k) } if @remove_keys
181
+
182
+ new_record
183
+ end
184
+
185
+ def expand_placeholders(value)
186
+ if value.is_a?(String)
187
+ new_value = @placeholder_expander.expand(value)
188
+ elsif value.is_a?(Hash)
189
+ new_value = {}
190
+ value.each_pair do |k, v|
191
+ new_value[@placeholder_expander.expand(k, true)] = expand_placeholders(v)
192
+ end
193
+ elsif value.is_a?(Array)
194
+ new_value = []
195
+ value.each_with_index do |v, i|
196
+ new_value[i] = expand_placeholders(v)
197
+ end
198
+ else
199
+ new_value = value
200
+ end
201
+ new_value
202
+ end
203
+
204
+ def tag_prefix(tag_parts)
205
+ return [] if tag_parts.empty?
206
+ tag_prefix = [tag_parts.first]
207
+ 1.upto(tag_parts.size-1).each do |i|
208
+ tag_prefix[i] = "#{tag_prefix[i-1]}.#{tag_parts[i]}"
209
+ end
210
+ tag_prefix
211
+ end
212
+
213
+ def tag_suffix(tag_parts)
214
+ return [] if tag_parts.empty?
215
+ rev_tag_parts = tag_parts.reverse
216
+ rev_tag_suffix = [rev_tag_parts.first]
217
+ 1.upto(tag_parts.size-1).each do |i|
218
+ rev_tag_suffix[i] = "#{rev_tag_parts[i]}.#{rev_tag_suffix[i-1]}"
219
+ end
220
+ rev_tag_suffix.reverse!
221
+ end
222
+
223
+ class PlaceholderExpander
224
+ attr_reader :placeholders, :log
225
+
226
+ def initialize(params)
227
+ @log = params[:log]
228
+ @auto_typecast = params[:auto_typecast]
229
+ end
230
+
231
+ def prepare_placeholders(time, record, opts)
232
+ placeholders = { '${time}' => Time.at(time).to_s }
233
+ record.each {|key, value| crawl_placeholder(value, placeholders, "#{key}")
234
+ }
235
+ opts.each do |key, value|
236
+ if value.kind_of?(Array) # tag_parts, etc
237
+ size = value.size
238
+ value.each_with_index { |v, idx|
239
+ placeholders.store("${#{key}[#{idx}]}", v)
240
+ placeholders.store("${#{key}[#{idx-size}]}", v) # support [-1]
241
+ }
242
+ else # string, interger, float, and others?
243
+ placeholders.store("${#{key}}", value)
244
+ end
245
+ end
246
+
247
+ @placeholders = placeholders
248
+ end
249
+
250
+ def crawl_placeholder (value, placeholder, before, limit = 50)
251
+ if limit >= 0
252
+ if value.kind_of?(Hash)
253
+ value.each {|key, v| crawl_placeholder(v, placeholder, "#{before}.#{key}", limit - 1)}
254
+ elsif value.kind_of?(Array) # tag_parts, etc
255
+ size = value.size
256
+ value.each_with_index { |v, idx|
257
+ crawl_placeholder(v, placeholder, "#{before}[#{idx}]", limit - 1)
258
+ crawl_placeholder(v, placeholder, "#{before}[#{idx-size}]", limit - 1) #suport [-1]
259
+ }
260
+ end
261
+ end
262
+ # string, interger, float, and others?
263
+ placeholder.store("${#{before}}", value)
264
+ end
265
+
266
+ def expand(str, force_stringify=false)
267
+ if @auto_typecast and !force_stringify
268
+ single_placeholder_matched = str.match(/\A(\${[^}]+}|__[A-Z_]+__)\z/)
269
+ if single_placeholder_matched
270
+ log_unknown_placeholder($1)
271
+ return @placeholders[single_placeholder_matched[1]]
272
+ end
273
+ end
274
+ str.gsub(/(\${[^}]+}|__[A-Z_]+__)/) {
275
+ log_unknown_placeholder($1)
276
+ @placeholders[$1]
277
+ }
278
+ end
279
+
280
+ private
281
+ def log_unknown_placeholder(placeholder)
282
+ unless @placeholders.include?(placeholder)
283
+ log.warn "unknown placeholder `#{placeholder}` found"
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-http-record-modifier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Augusto Nishi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
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: fluentd
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
+ description: fluentd filter plugin for modifing record based on a HTTP request
56
+ email:
57
+ - augusto.nishi@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - fluent-plugin-http-record-modifier.gemspec
68
+ - lib/fluent/plugin/.filter_http_record_modifier.rb.swp
69
+ - lib/fluent/plugin/filter_http_record_modifier.rb
70
+ homepage: http://github.com/DEVTecnologia/fluent-plugin-http-record-modifier
71
+ licenses:
72
+ - MIT
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.2.1
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: fluentd filter plugin for modifing record based on a HTTP request
94
+ test_files: []