logstash-input-httpclient 0.2.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/logstash/inputs/httpclient.rb +160 -99
- data/logstash-input-httpclient.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5937770c62c0b1d858d952beaf403efcf716b49
|
4
|
+
data.tar.gz: d505ba3fece17a54e9746dc6550bcc9c271a2fb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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.
|
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.
|
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-
|
11
|
+
date: 2015-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logstash-core
|