fluent-plugin-http-record-modifier 0.0.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 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: []