fluent-plugin-out-http-ext 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,2 @@
1
- service_name: travis-pro
1
+ service_name: travis-ci
2
2
  repo_token: 9tG18WMwC1xpPdDBEGznon01RCp9NusHL
data/README.md CHANGED
@@ -15,8 +15,11 @@ A generic [fluentd][1] output plugin for sending logs to an HTTP endpoint
15
15
  http_method put # default: post
16
16
  serializer json # default: form
17
17
  rate_limit_msec 100 # default: 0 = no rate limiting
18
+ open_timeout 5 # default: nil = no timeout
19
+ read_timeout 10 # default: 60
18
20
  raise_on_error false # default: true
19
21
  raise_on_http_failure true # default: false
22
+ ignore_http_status_code 300,400..499 # default: nil # do not raise on these http_hstatus codes
20
23
  authentication basic # default: none
21
24
  username alice # default: ''
22
25
  password bobpop # default: '', secret: true
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "fluent-plugin-out-http-ext"
5
- gem.version = "0.1.6"
5
+ gem.version = "0.1.7"
6
6
  gem.authors = ["Toshiya Kawasaki"]
7
7
  gem.email = ["kawasakitoshiya@gmail.com"]
8
8
  gem.summary = %q{A generic Fluentd output plugin to send logs to an HTTP endpoint with SSL and Header option}
@@ -1,3 +1,12 @@
1
+ require 'set'
2
+
3
+ class Array
4
+ def to_set
5
+ Set.new(self)
6
+ end
7
+ end
8
+
9
+
1
10
  class Hash
2
11
  """
3
12
  each traverse in hash
@@ -19,6 +28,39 @@ class Hash
19
28
 
20
29
  end
21
30
 
31
+ class StatusCodeParser
32
+ """
33
+ parse status code string to array of codes
34
+ """
35
+ def self.range?(str)
36
+ # i.e. 200..399 => return true
37
+ return /^\d{3}..\d{3}$/ =~ str ? true : false
38
+ end
39
+
40
+ def self.number?(str)
41
+ return /^\d{3}$/ =~ str ? true : false
42
+ end
43
+
44
+ def self.get_array(str)
45
+ if self.range?(str)
46
+ ends = str.split('..').map{|d| Integer(d)}
47
+ return (ends[0]..ends[1]).to_a
48
+ elsif self.number?(str)
49
+ return [str.to_i]
50
+ else
51
+ raise "invalid status code range format"
52
+ end
53
+ end
54
+
55
+ def self.convert(range_str)
56
+ elems = range_str.split(',')
57
+ status_codes = elems.flat_map do |elem|
58
+ self.get_array(elem)
59
+ end
60
+ return status_codes.to_set
61
+ end
62
+ end
63
+
22
64
  class Fluent::HTTPOutput < Fluent::Output
23
65
  Fluent::Plugin.register_output('http_ext', self)
24
66
 
@@ -27,6 +69,7 @@ class Fluent::HTTPOutput < Fluent::Output
27
69
  require 'net/http'
28
70
  require 'uri'
29
71
  require 'yajl'
72
+ require 'set'
30
73
  end
31
74
 
32
75
  # Endpoint URL ex. localhost.local/api/
@@ -41,6 +84,9 @@ class Fluent::HTTPOutput < Fluent::Output
41
84
  # true | false
42
85
  config_param :use_ssl, :bool, :default => false
43
86
 
87
+ config_param :open_timeout, :integer, :default => nil
88
+ config_param :read_timeout, :integer, :default => 60
89
+
44
90
  # Simple rate limiting: ignore any records within `rate_limit_msec`
45
91
  # since the last one.
46
92
  config_param :rate_limit_msec, :integer, :default => 0
@@ -50,8 +96,7 @@ class Fluent::HTTPOutput < Fluent::Output
50
96
 
51
97
  # Raise errors when HTTP response code was not successful.
52
98
  config_param :raise_on_http_failure, :bool, :default => false
53
-
54
-
99
+ config_param :ignore_http_status_code, :string, :default => nil
55
100
  # nil | 'none' | 'basic'
56
101
  config_param :authentication, :string, :default => nil
57
102
  config_param :username, :string, :default => ''
@@ -74,6 +119,12 @@ class Fluent::HTTPOutput < Fluent::Output
74
119
  :post
75
120
  end
76
121
 
122
+ @ignore_http_status_code = if @ignore_http_status_code.nil?
123
+ [].to_set
124
+ else
125
+ StatusCodeParser.convert(@ignore_http_status_code)
126
+ end
127
+
77
128
  @auth = case @authentication
78
129
  when 'basic' then :basic
79
130
  else
@@ -158,7 +209,11 @@ class Fluent::HTTPOutput < Fluent::Output
158
209
  client.use_ssl = true
159
210
  client.ca_file = OpenSSL::X509::DEFAULT_CERT_FILE
160
211
  end
161
- res = client.start {|http| http.request(req) }
212
+ res = client.start {|http|
213
+ http.open_timeout = @open_timeout
214
+ http.read_timeout = @read_timeout
215
+ http.request(req)
216
+ }
162
217
  rescue => e # rescue all StandardErrors
163
218
  # server didn't respond
164
219
  $log.warn "Net::HTTP.#{req.method.capitalize} raises exception: #{e.class}, '#{e.message}'"
@@ -172,7 +227,14 @@ class Fluent::HTTPOutput < Fluent::Output
172
227
  end
173
228
  warning = "failed to #{req.method} #{uri} (#{res_summary})"
174
229
  $log.warn warning
175
- raise warning if @raise_on_http_failure
230
+ if @raise_on_http_failure
231
+ unless @ignore_http_status_code.include?(res.code.to_i)
232
+ raise warning
233
+ else
234
+ $log.debug "ignore http status code #{req.method}"
235
+ end
236
+ end
237
+
176
238
  end #end unless
177
239
  end # end begin
178
240
  end # end send_request
@@ -59,6 +59,23 @@ class HTTPOutputTestBase < Test::Unit::TestCase
59
59
  res.status = 200
60
60
  res.body = 'running'
61
61
  }
62
+ srv.mount_proc('/slow_5') { |req,res|
63
+ sleep 5
64
+ res.status = 200
65
+ res.body = 'slow_5'
66
+ }
67
+ srv.mount_proc('/slow_10') { |req,res|
68
+ sleep 10
69
+ res.status = 200
70
+ res.body = 'slow_10'
71
+ }
72
+ srv.mount_proc('/status_code') { |req,res|
73
+ r = Yajl.load(req.body)
74
+ code = r["code"]
75
+ res.status = code.to_s
76
+ res.body = ''
77
+ }
78
+
62
79
  srv.start
63
80
  ensure
64
81
  srv.shutdown
@@ -169,6 +186,38 @@ class HTTPOutputTest < HTTPOutputTestBase
169
186
  rate_limit_msec #{RATE_LIMIT_MSEC}
170
187
  ]
171
188
 
189
+ CONFIG_NOT_READ_TIMEOUT = %[
190
+ endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/slow_5/
191
+ read_timeout 7
192
+ ]
193
+ CONFIG_READ_TIMEOUT = %[
194
+ endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/slow_10/
195
+ read_timeout 7
196
+ ]
197
+ CONFIG_IGNORE_NONE = %[
198
+ endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
199
+ serializer json
200
+ raise_on_http_failure true
201
+ ]
202
+ CONFIG_IGNORE_409 = %[
203
+ endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
204
+ serializer json
205
+ raise_on_http_failure true
206
+ ignore_http_status_code 409
207
+ ]
208
+ CONFIG_IGNORE_4XX = %[
209
+ endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
210
+ serializer json
211
+ raise_on_http_failure true
212
+ ignore_http_status_code 400..499
213
+ ]
214
+ CONFIG_IGNORE_4XX_5XX = %[
215
+ endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
216
+ serializer json
217
+ raise_on_http_failure true
218
+ ignore_http_status_code 400..599
219
+ ]
220
+
172
221
  def create_driver(conf=CONFIG, tag='test.metrics')
173
222
  Fluent::Test::OutputTestDriver.new(Fluent::HTTPOutput, tag).configure(conf)
174
223
  end
@@ -302,6 +351,93 @@ class HTTPOutputTest < HTTPOutputTestBase
302
351
  assert_equal 2, @posts.size
303
352
  end
304
353
 
354
+ def test_read_timeout
355
+ d = create_driver CONFIG_READ_TIMEOUT
356
+ assert_equal 7, d.instance.read_timeout
357
+ err = Net.const_defined?(:ReadTimeout) ? Net::ReadTimeout : Timeout::Error
358
+ assert_raise err do
359
+ d.emit({})
360
+ d.run
361
+ end
362
+ end
363
+
364
+ def test_not_read_timeout
365
+ d = create_driver CONFIG_NOT_READ_TIMEOUT
366
+ assert_equal 7, d.instance.read_timeout
367
+ assert_nothing_raised do
368
+ d.emit({})
369
+ d.run
370
+ end
371
+ end
372
+
373
+ def test_ignore_none
374
+ d = create_driver CONFIG_IGNORE_NONE
375
+ assert_equal [].to_set, d.instance.ignore_http_status_code
376
+
377
+ assert_raise do
378
+ d.emit({:code=> 409})
379
+ d.run
380
+ end
381
+
382
+ assert_raise do
383
+ d.emit({:code => 500})
384
+ d.run
385
+ end
386
+ end
387
+
388
+ def test_ignore_409
389
+ d = create_driver CONFIG_IGNORE_409
390
+ assert_equal [409].to_set, d.instance.ignore_http_status_code
391
+
392
+ assert_nothing_raised do
393
+ d.emit({:code => 409})
394
+ d.run
395
+ end
396
+ assert_raise do
397
+ d.emit({:code => 404})
398
+ d.run
399
+ end
400
+ assert_raise do
401
+ d.emit({:code => 500})
402
+ d.run
403
+ end
404
+ end
405
+
406
+ def test_ignore_4XX
407
+ d = create_driver CONFIG_IGNORE_4XX
408
+ assert_equal (400..499).to_a.to_set, d.instance.ignore_http_status_code
409
+
410
+ assert_nothing_raised do
411
+ d.emit({:code => 409})
412
+ d.run
413
+ end
414
+ assert_nothing_raised do
415
+ d.emit({:code => 404})
416
+ d.run
417
+ end
418
+ assert_raise do
419
+ d.emit({:code => 500})
420
+ d.run
421
+ end
422
+ end
423
+
424
+ def test_ignore_4XX_5XX
425
+ d = create_driver CONFIG_IGNORE_4XX_5XX
426
+ assert_equal (400..599).to_a.to_set, d.instance.ignore_http_status_code
427
+ assert_nothing_raised do
428
+ d.emit({:code => 409})
429
+ d.run
430
+ end
431
+ assert_nothing_raised do
432
+ d.emit({:code => 404})
433
+ d.run
434
+ end
435
+ assert_nothing_raised do
436
+ d.emit({:code => 500})
437
+ d.run
438
+ end
439
+ end
440
+
305
441
  def _current_msec
306
442
  Time.now.to_f * 1000
307
443
  end
@@ -339,4 +475,37 @@ class HTTPOutputTest < HTTPOutputTestBase
339
475
  assert_equal 2, @prohibited
340
476
  end
341
477
 
478
+ def test_status_code_parser()
479
+ assert_equal (400..409).to_a.to_set, StatusCodeParser.convert("400..409")
480
+ assert_equal ((400..409).to_a + [300]).to_set, StatusCodeParser.convert("400..409,300")
481
+ assert_equal ((400..409).to_a + [300]).to_set, StatusCodeParser.convert("300,400..409")
482
+ assert_equal [404, 409].to_set, StatusCodeParser.convert("404,409")
483
+ assert_equal [404, 409, 300, 301, 302, 303].to_set, StatusCodeParser.convert("404,409,300..303")
484
+ assert_equal [409].to_set, StatusCodeParser.convert("409")
485
+ assert_equal [].to_set, StatusCodeParser.convert("")
486
+ assert_raise do
487
+ StatusCodeParser.convert("400...499")
488
+ end
489
+ assert_raise do
490
+ StatusCodeParser.convert("10..20")
491
+ end
492
+ assert_raise do
493
+ StatusCodeParser.convert("4XX")
494
+ end
495
+ assert_raise do
496
+ StatusCodeParser.convert("4XX..5XX")
497
+ end
498
+ assert_raise do
499
+ StatusCodeParser.convert("200.0..400")
500
+ end
501
+ assert_raise do
502
+ StatusCodeParser.convert("-200..400")
503
+ end
504
+
505
+ end
506
+
507
+ def test_array_extend
508
+ assert_equal [].to_set, Set.new([])
509
+ assert_equal [1, 2].to_set, Set.new([1, 2])
510
+ end
342
511
  end
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-out-http-ext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Toshiya Kawasaki
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-12-16 00:00:00.000000000 Z
12
+ date: 2016-01-14 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: yajl-ruby
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,8 +30,9 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: fluentd
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: 0.10.0
34
38
  - - <
@@ -37,8 +41,9 @@ dependencies:
37
41
  type: :runtime
38
42
  prerelease: false
39
43
  version_requirements: !ruby/object:Gem::Requirement
44
+ none: false
40
45
  requirements:
41
- - - '>='
46
+ - - ! '>='
42
47
  - !ruby/object:Gem::Version
43
48
  version: 0.10.0
44
49
  - - <
@@ -47,57 +52,65 @@ dependencies:
47
52
  - !ruby/object:Gem::Dependency
48
53
  name: bundler
49
54
  requirement: !ruby/object:Gem::Requirement
55
+ none: false
50
56
  requirements:
51
- - - '>='
57
+ - - ! '>='
52
58
  - !ruby/object:Gem::Version
53
59
  version: '0'
54
60
  type: :development
55
61
  prerelease: false
56
62
  version_requirements: !ruby/object:Gem::Requirement
63
+ none: false
57
64
  requirements:
58
- - - '>='
65
+ - - ! '>='
59
66
  - !ruby/object:Gem::Version
60
67
  version: '0'
61
68
  - !ruby/object:Gem::Dependency
62
69
  name: rake
63
70
  requirement: !ruby/object:Gem::Requirement
71
+ none: false
64
72
  requirements:
65
- - - '>='
73
+ - - ! '>='
66
74
  - !ruby/object:Gem::Version
67
75
  version: '0'
68
76
  type: :development
69
77
  prerelease: false
70
78
  version_requirements: !ruby/object:Gem::Requirement
79
+ none: false
71
80
  requirements:
72
- - - '>='
81
+ - - ! '>='
73
82
  - !ruby/object:Gem::Version
74
83
  version: '0'
75
84
  - !ruby/object:Gem::Dependency
76
85
  name: test-unit
77
86
  requirement: !ruby/object:Gem::Requirement
87
+ none: false
78
88
  requirements:
79
- - - '>='
89
+ - - ! '>='
80
90
  - !ruby/object:Gem::Version
81
91
  version: 3.1.0
82
92
  type: :development
83
93
  prerelease: false
84
94
  version_requirements: !ruby/object:Gem::Requirement
95
+ none: false
85
96
  requirements:
86
- - - '>='
97
+ - - ! '>='
87
98
  - !ruby/object:Gem::Version
88
99
  version: 3.1.0
89
100
  - !ruby/object:Gem::Dependency
90
101
  name: coveralls
91
102
  requirement: !ruby/object:Gem::Requirement
103
+ none: false
92
104
  requirements:
93
- - - '>='
105
+ - - ! '>='
94
106
  - !ruby/object:Gem::Version
95
107
  version: '0'
96
108
  type: :development
97
109
  prerelease: false
98
110
  version_requirements: !ruby/object:Gem::Requirement
111
+ none: false
99
112
  requirements:
100
- - - '>='
113
+ - - ! '>='
101
114
  - !ruby/object:Gem::Version
102
115
  version: '0'
103
116
  description: A generic Fluentd output plugin to send logs to an HTTP endpoint with
@@ -124,26 +137,27 @@ files:
124
137
  homepage:
125
138
  licenses:
126
139
  - Apache-2.0
127
- metadata: {}
128
140
  post_install_message:
129
141
  rdoc_options: []
130
142
  require_paths:
131
143
  - lib
132
144
  required_ruby_version: !ruby/object:Gem::Requirement
145
+ none: false
133
146
  requirements:
134
- - - '>='
147
+ - - ! '>='
135
148
  - !ruby/object:Gem::Version
136
149
  version: '0'
137
150
  required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
138
152
  requirements:
139
- - - '>='
153
+ - - ! '>='
140
154
  - !ruby/object:Gem::Version
141
155
  version: '0'
142
156
  requirements: []
143
157
  rubyforge_project:
144
- rubygems_version: 2.4.8
158
+ rubygems_version: 1.8.23
145
159
  signing_key:
146
- specification_version: 4
160
+ specification_version: 3
147
161
  summary: A generic Fluentd output plugin to send logs to an HTTP endpoint with SSL
148
162
  and Header option
149
163
  test_files:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 3e9c08d1a809ddd3df73b5ab13fe55ac7ed79305
4
- data.tar.gz: 2af17adfcbb3375ab1079dfcc56d51d2a64d0b06
5
- SHA512:
6
- metadata.gz: 4c445f8eb6d35bf52705eeb683ff48f15d96b5eadc8625736ce98cd2b52d9f207a4e08ba6abd2ac8306b025d0626eae55e3204ab3806638fdd4468c19b7699d4
7
- data.tar.gz: 66977734544084539b681b6690e82fef9cb595badfb57cd509f97f1cdf196afd3e314e0697bda3a25175f119bdb78b278237ee8ee1c7d2e3e515bb50d99f7bb6