logstash-input-httpclient 0.2.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34f259c605e9064ef201bcf35e71b9aa20b82dc8
4
- data.tar.gz: b9d6f9eab9ad654bc368dbd708f7f7ad7193cf01
3
+ metadata.gz: e5937770c62c0b1d858d952beaf403efcf716b49
4
+ data.tar.gz: d505ba3fece17a54e9746dc6550bcc9c271a2fb0
5
5
  SHA512:
6
- metadata.gz: f58d38b040dda5c5bd5fbdbeb91b8438ac2c1962c1164f49b7d18c822cff00c76bf5822250b3ab55822db38f9005aefd8ecc5e60344d06fe1b6100c78d7b7525
7
- data.tar.gz: 69193c447b6aa56d3cfc7c03a1c829c46d76f0e6197f2e1a3fce12d46e0e820694fb999619896bcbd0a96b6cbdf7e81acbaa37d8f89b0789dadadbf682ce1b97
6
+ metadata.gz: 6dbe5f66ee634e65bb45fae4705af0dc11094bafcb7ec967aad550f3395094eb77a28482656df653b2e11144da4f3696a0c16918fb001806349f8fd68b896abc
7
+ data.tar.gz: 79c3b89b1fca5643b5ccf5e5c2b83148904a3f06fb42f6fc737fa54457ce4f5d6e2d86d779192acbb8454cd5b92bcf12940c91d86f2d1bc9ce987859daa64842
@@ -4,116 +4,177 @@ require "logstash/namespace"
4
4
  require "stud/interval"
5
5
  require "net/http"
6
6
 
7
- # Query a http server with GET requests at a regular interval.
7
+ # Query a HTTP server with GET requests at a regular interval and process the events returned in the response body.
8
8
  #
9
9
  #[%hardbreaks]
10
10
  # HTTP GET requests are performed using the provided url (which may contain URL parameters).
11
- # The HTTP Response body will be added to "message" after being decoded by the provided codec.
11
+ # The HTTP Response body will be decoded by the provided coded and the events will be sent to logstash for filtering/output.
12
+ #
13
+ # HTTP Header Information (advanced use cases):
14
+ # There are several additional HTTP headers that can be useful if needed (otherwise they can be safely ignored):
15
+ #
16
+ # **Response Headers** (from the server)
17
+ # `X-More-Events-Available` If set to `true`, then there will be no wait time before the next HTTP GET Request is executed to get more events.
18
+ # `X-Messages-Batch-Id` Can be set to any value to identify this 'batch' of messages. The client will add a `X-Successful-Batch-Ids` header to the next request if this batch is successfully sent to logstash.
19
+ #
20
+ # **Request Headers** (these provide stats/info about the **previous** HTTP Request and the events processed from it)
21
+ # `X-Logstash-Num-Events` The number of events that were processed from the previous request
22
+ # `X-Logstash-Http-Request-Total-Secs` The time (in seconds) that it took for the HTTP Request to finish.
23
+ # `X-Logstash-Codec-Parse-Total-Secs` The time (in seconds) that it took for the HTTP Response body to be parsed and decoded.
24
+ # `X-Logstash-Queue-Total-Secs` The time (in seconds) that it took to queue all of the events (logstash uses a fixed queue length of 20, so it blocks when full).
25
+ # `X-Successful-Batch-Ids` If the server provided a `X-Messages-Batch-Id` header in the previous request, that value is set in this header to acknowledge the messages were successfully sent to logstash.
26
+
12
27
  # There is an additional header named `X-Logstash-Avg-Queue-Secs` added to each HTTP request. This is the
13
28
  # average seconds it took for the past 20 events to be accepted by Logstash's bounded input queue.
14
29
  # The HTTP server could use this as a indication of how many events the Logstash instance is able to process (throughput).
15
30
 
16
31
 
17
32
  class LogStash::Inputs::HttpClient < LogStash::Inputs::Base
18
- config_name "httpclient"
19
-
20
- # If undefined, Logstash will complain, even if codec is unused.
21
- default :codec, "plain"
22
-
23
- #[%hardbreaks]
24
- # The full url to execute GET requests on.
25
- # It may contain URL parameters (you are responsible for URL encoding them)
26
- # e.g. `https://mywebservice.int/`
27
- # e.g. `http://mywebservice.int?queue=logstash_events&numevents=10`
28
- #
29
- # IMPORTANT: make sure you end with a slash (if not using url params). `http://mywebservice.int` is not valid, it must be `http://myswebservice.int/`
30
- # todo: auto-append slash if user forgets
31
- config :url, :validate => :string, :required => true
33
+ config_name "httpclient"
32
34
 
33
- # Set how frequently we should query the url for messages
34
- #
35
- # The default, `10`, means send a message every 10 seconds.
36
- config :interval, :validate => :number, :default => 10
37
-
38
- # The name of the object that http response meta-data (headers, response code, etc) should be added to.
39
- config :response_object_name, :validate => :string, :default => "http_response"
40
-
41
- # Should the http headers be added to the `response_object_name`?
42
- #
43
- # If true, there will be a "headers" with all the response headers/values.
44
- config :include_response_headers, :validate => :boolean, :default => false
45
-
46
- # Should the http response code be added to the `response_object_name`?
47
- #
48
- # If true, there will be a "code" with all the response headers/values.
49
- config :include_response_code, :validate => :boolean, :default => true
50
-
51
- # Should the http request elapsed time be added to the `response_object_name`?
52
- #
53
- # If true, there will be a "took_secs" with the time it took for the http request to complete
54
- config :include_http_request_time, :validate => :boolean, :default => true
55
-
56
- public
57
- def register
58
- @uri = URI(@url)
59
- end # def register
35
+ # If undefined, Logstash will complain, even if codec is unused.
36
+ default :codec, "plain"
60
37
 
61
- def run(queue)
62
- queue_times = Array.new #track an average of how long it's taking to queue events into logstash
63
- #::start creates a connection to the HTTP server and keeps it alive for the duration
64
- Net::HTTP.start @uri.host, @uri.port, :use_ssl => @uri.scheme == 'https' do |http|
65
- while true
66
- begin
67
- sleepIval = @interval
68
- begin
69
- http_start = Time.now
70
- request = Net::HTTP::Get.new(@uri.request_uri)
71
- request ["X-Logstash-Avg-Queue-Secs"] = arr_avg(queue_times, 20, 3)
72
- response = http.request request # Net::HTTPResponse object
73
- http_elapsed = Time.now - http_start
74
- rescue => e
75
- @logger.warn("Http request failed, will retry", :exception => e)
76
- @logger.warn(e.backtrace)
77
- sleep(sleepIval)
78
- retry
79
- end
38
+ #[%hardbreaks]
39
+ # The full url to execute GET requests on.
40
+ # It may contain URL parameters (you are responsible for URL encoding them)
41
+ # e.g. `https://mywebservice.mycompany.int/`
42
+ # e.g. `http://mywebservice.mycompany.int?type=logstash_events&numevents=10`
43
+ #
44
+ config :url, :validate => :string, :required => true
80
45
 
81
- if response["X-More-Events-Available"] == "true"
82
- sleepIval = 0 #don't wait if the server indicated there are more events ready now
83
- end
46
+ # Set how frequently we should query the url for messages
47
+ #
48
+ # The default, `10`, means execute a HTTP GET request every 10 seconds.
49
+ # If the server adds a `X-More-Events-Available: true` header to the response,
50
+ #a new request will be executed immediately and this wait time will be ignored.
51
+ config :interval, :validate => :number, :default => 10
52
+
53
+ # If desired, meta-data about the HTTP Response can be included in the event.
54
+ # This is the name of the object that http response meta-data (headers, response code, etc) will be added to.
55
+ config :response_object_name, :validate => :string, :default => "http_response"
56
+
57
+ # Should the http response headers be added to the `response_object_name`?
58
+ #
59
+ # If true, there will be a "headers" object with all the response headers/values.
60
+ config :include_response_headers, :validate => :boolean, :default => false
61
+
62
+ # Should the http response code be added to the `response_object_name`?
63
+ #
64
+ # If true, there will be a "code" with the response code.
65
+ config :include_response_code, :validate => :boolean, :default => true
66
+
67
+ # Should the http request elapsed time be added to the `response_object_name`?
68
+ #
69
+ # If true, there will be a "took_secs" with the time it took for the http request to complete
70
+ config :include_http_request_time, :validate => :boolean, :default => true
71
+
72
+ # The server should return this HTTP response code to indicate no messages were found/retuned.
73
+ # When this status code is seen, the message body will not be processed or added to the Logstash input queue.
74
+ # Defaults to 204 (No Content)
75
+ config :no_messages_response_code, :validate => :number, :default => 204
76
+
77
+ public
78
+ def register
79
+ @uri = URI(@url)
80
+ end # def register
84
81
 
85
- @codec.decode(response.body) do |event|
86
- event[@response_object_name] = {}
87
- if @include_response_headers
88
- event[@response_object_name]["headers"] = response
89
- end
90
- if @include_response_code
91
- event[@response_object_name]["code"] = response.code
92
- end
93
- if @include_http_request_time
94
- event[@response_object_name]["took_secs"] = http_elapsed
95
- end
96
- decorate(event)
97
- queue_start = Time.now
98
- queue << event
99
- queue_elapsed = Time.now - queue_start
100
- queue_times.push queue_elapsed
101
- end
102
- ensure
103
- sleep(@interval)
104
- end
105
- end #interval loop
106
- end #HTTP keepalive
107
- end # def run
108
-
109
- def arr_avg(arr,cutoff,precision)
110
- if(arr.size == 0)
111
- return 0
112
- end
113
- if(arr.size > cutoff)
114
- arr.drop(arr.length-cutoff) #removes from beginning of array
115
- end
116
- #get the average
117
- (arr.inject{ |sum, el| sum + el }.to_f / arr.size).round(precision)
118
- end
82
+ def run(queue)
83
+ begin
84
+ num_events = 0
85
+ queue_time = nil #track an average of how long it's taking to queue events into logstash
86
+ codec_parse_time = nil
87
+ http_request_time = nil
88
+ batch_id = nil
89
+
90
+ @logger.debug("Initializing http connection")
91
+ #Net::HTTP.start creates a connection to the HTTP server and keeps it alive for the duration. Throws an EOFError when connection breaks.
92
+ Net::HTTP.start @uri.host, @uri.port, :use_ssl => @uri.scheme == 'https' do |http|
93
+ while true
94
+ http_start = Time.now
95
+ sleepIval = @interval
96
+ @logger.debug("Executing http get request: " + @uri.host + @uri.request_uri)
97
+ request = Net::HTTP::Get.new(@uri.request_uri)
98
+
99
+ #include stats from processing the previous batch as custom headers
100
+ if num_events > 0 #0 if there wasn't anything processed in the last batch
101
+ #send the times/stats (from the previous batch) as custom headers to the server
102
+ request ["X-Logstash-Num-Events"] = num_events
103
+ request ["X-Logstash-Http-Request-Total-Secs"] = http_request_time
104
+ request ["X-Logstash-Codec-Parse-Total-Secs"] = codec_parse_time
105
+ request ["X-Logstash-Queue-Total-Secs"] = queue_time
106
+ end
107
+ if !batch_id.nil?
108
+ request ["X-Successful-Batch-Ids"] = batch_id #send the previous successfully processed batch id so the server knows those messages were all processed by logstash
109
+ end
110
+ response = http.request request # execute GET request and return Net::HTTPResponse object
111
+ if response["X-More-Events-Available"] == "true"
112
+ sleepIval = 0 #don't wait if the server indicated there are more events ready now
113
+ end
114
+ batch_id = response["X-Messages-Batch-Id"] #save the batch id of the event in this response body, so it can be ack'd in the next http request
115
+ http_request_time = Time.now - http_start
116
+
117
+
118
+ #check if events were returned and process them using the defined @codec
119
+ num_events = 0
120
+ if response.code.to_i == @no_messages_response_code
121
+ #HTTP Response code indicated there are no messages. Skip processing response and sleep.
122
+ batch_id = nil
123
+ elsif response.code.to_i != 200
124
+ @logger.warn("Received non-200 response code: #{response.code}. Will not process this response.")
125
+ elsif response.body.nil?
126
+ @logger.warn("Received normal #{response.code} response code, but the response body is empty. Will not process this response.")
127
+ else #normal HTTP 200 OK response body -- process this response
128
+ codec_start = Time.now
129
+ events = Array.new #will store the decoded & decorated event(s) from the message body in this temporary in-memory array
130
+ @codec.decode(response.body) do |event|
131
+ event[@response_object_name] = {}
132
+ if @include_response_headers
133
+ event[@response_object_name]["headers"] = response
134
+ end
135
+ if @include_response_code
136
+ event[@response_object_name]["code"] = response.code.to_i
137
+ end
138
+ if @include_http_request_time
139
+ event[@response_object_name]["took_secs"] = http_request_time
140
+ end
141
+ decorate(event)
142
+ events.push(event)
143
+ end #end decoding events in response body
144
+ num_events = events.length
145
+ codec_parse_time = Time.now - codec_start
146
+
147
+ #now queue the events to Logstash's bounded input queue and time how long it takes
148
+ queue_start = Time.now
149
+ events.each{ |event|
150
+ #@logger.info("Queueing event")
151
+ queue << event #blocks if queue is full
152
+ #@logger.info("Event has been queued")
153
+ }
154
+ queue_time = Time.now - queue_start
155
+ @logger.info("HTTP GET request processed #{events.length} events. http_request_secs=#{http_request_time}, codec_parse_secs=#{codec_parse_time}, queueing_secs=#{queue_time}, queue_size=#{queue.length}/#{queue.max}, queue_num_threads_waiting=#{queue.num_waiting}. Server=" + response["Server"] + ". Request=" + @uri.host + @uri.request_uri)
156
+ end #end normal processing of HTTP response body
157
+
158
+ #TODO: should we provide an option to post back (ack) immetiately once we sucecssfully queue all the messages in the response
159
+
160
+ if(sleepIval > 0)
161
+ @logger.debug("Waiting #{sleepIval} seconds before next http request")
162
+ sleep(sleepIval)
163
+ end
164
+ end #while loop
165
+ end #HTTP keepalive
166
+ rescue => x
167
+ if x.instance_of?(EOFError)
168
+ #this seems to happen if the http keepalive times out and server disconnects
169
+ @logger.warn("HTTP Connection has closed (EOFError), will reset http client connection and try again.", :exception => x)
170
+ sleep(1)
171
+ else
172
+ @logger.warn("Error occurred, resetting http client connection and trying again in 10 seconds.", :exception => x)
173
+ @logger.warn(x.backtrace)
174
+ sleep(10) #todo: configurable sleep time for errors?
175
+ end
176
+ retry
177
+ end #end begin outside of the loop
178
+ @logger.warn("Unexpected: HTTP client has ended")
179
+ end # def run
119
180
  end # class LogStash::Inputs::Example
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-httpclient'
3
- s.version = '0.2.0'
3
+ s.version = '0.9.0'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "This input queries a http url at a regular interval and sends the responses to logstash."
6
6
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-httpclient
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bradvido
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-08 00:00:00.000000000 Z
11
+ date: 2015-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-core