logstash-filter-dns 2.0.2 → 2.1.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +3 -0
- data/lib/logstash/filters/dns.rb +93 -44
- data/logstash-filter-dns.gemspec +2 -1
- data/spec/filters/dns_spec.rb +123 -3
- metadata +25 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4665d1daf1b87421818c9e6bd11f6efadb74349c
|
4
|
+
data.tar.gz: 4dcb49b8c0e902156fa2ebc3aadd9e97e3b7252f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87b081e655bd9eb3e2a939cea5a1464328e7225ac655f30ae4fc4a8702b026715bf15576d642baeea467d9801272980f9555c669f5cfbd928e9a3b0abd1d3c13
|
7
|
+
data.tar.gz: 3d18bbc3ae5ae1661763334793b199fea6a07405e2b869ad19614906f1c06ca9382ae7014d0fbb74bbc47f8c380f955b52e6b08b52a5bc7490217e812a895b04
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 2.1.0
|
2
|
+
- Add caches for failed and successful lookups
|
3
|
+
- Lower default timeout value
|
4
|
+
- Retry a maximum of :max_retries instead of failing immediately
|
5
|
+
|
1
6
|
## 2.0.0
|
2
7
|
- Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
|
3
8
|
instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Logstash Plugin
|
2
2
|
|
3
|
+
[](http://build-eu-00.elastic.co/view/LS%20Plugins/view/LS%20Filters/job/logstash-plugin-filter-dns-unit/)
|
5
|
+
|
3
6
|
This is a plugin for [Logstash](https://github.com/elastic/logstash).
|
4
7
|
|
5
8
|
It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
|
data/lib/logstash/filters/dns.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "logstash/filters/base"
|
3
3
|
require "logstash/namespace"
|
4
|
+
require "lru_redux"
|
5
|
+
|
4
6
|
|
5
7
|
# The DNS filter performs a lookup (either an A record/CNAME record lookup
|
6
8
|
# or a reverse lookup at the PTR record) on records specified under the
|
@@ -18,7 +20,7 @@ require "logstash/namespace"
|
|
18
20
|
# }
|
19
21
|
#
|
20
22
|
# Caveats: Currently there is no way to specify a timeout in the DNS lookup.
|
21
|
-
#
|
23
|
+
#
|
22
24
|
# This filter, like all filters, only processes 1 event at a time, so the use
|
23
25
|
# of this plugin can significantly slow down your pipeline's throughput if you
|
24
26
|
# have a high latency network. By way of example, if each DNS lookup takes 2
|
@@ -46,7 +48,22 @@ class LogStash::Filters::DNS < LogStash::Filters::Base
|
|
46
48
|
config :nameserver, :validate => :array
|
47
49
|
|
48
50
|
# `resolv` calls will be wrapped in a timeout instance
|
49
|
-
config :timeout, :validate => :number, :default =>
|
51
|
+
config :timeout, :validate => :number, :default => 0.5
|
52
|
+
|
53
|
+
# number of times to retry a failed resolve/reverse
|
54
|
+
config :max_retries, :validate => :number, :default => 2
|
55
|
+
|
56
|
+
# set the size of cache for successful requests
|
57
|
+
config :hit_cache_size, :validate => :number, :default => 0
|
58
|
+
|
59
|
+
# how long to cache successful requests (in seconds)
|
60
|
+
config :hit_cache_ttl, :validate => :number, :default => 60
|
61
|
+
|
62
|
+
# cache size for failed requests (Resolv::
|
63
|
+
config :failed_cache_size, :validate => :number, :default => 0
|
64
|
+
|
65
|
+
# how long to cache failed requests (in seconds)
|
66
|
+
config :failed_cache_ttl, :validate => :number, :default => 5
|
50
67
|
|
51
68
|
public
|
52
69
|
def register
|
@@ -58,42 +75,29 @@ class LogStash::Filters::DNS < LogStash::Filters::Base
|
|
58
75
|
@resolv = Resolv.new(resolvers=[::Resolv::Hosts.new, ::Resolv::DNS.new(:nameserver => @nameserver, :search => [], :ndots => 1)])
|
59
76
|
end
|
60
77
|
|
78
|
+
if @hit_cache_size > 0
|
79
|
+
@hit_cache = LruRedux::ThreadSafeCache.new(@hit_cache_size, @hit_cache_ttl)
|
80
|
+
end
|
81
|
+
|
82
|
+
if @failed_cache_size > 0
|
83
|
+
@failed_cache = LruRedux::ThreadSafeCache.new(@failed_cache_size, @failed_cache_ttl)
|
84
|
+
end
|
85
|
+
|
61
86
|
@ip_validator = Resolv::AddressRegex
|
62
87
|
end # def register
|
63
88
|
|
64
89
|
public
|
65
90
|
def filter(event)
|
66
|
-
|
67
|
-
|
68
|
-
new_event = event.clone
|
69
91
|
|
70
92
|
if @resolve
|
71
|
-
|
72
|
-
status = Timeout::timeout(@timeout) {
|
73
|
-
resolve(new_event)
|
74
|
-
}
|
75
|
-
return if status.nil?
|
76
|
-
rescue Timeout::Error
|
77
|
-
@logger.debug("DNS: resolve action timed out")
|
78
|
-
return
|
79
|
-
end
|
93
|
+
return if resolve(event).nil?
|
80
94
|
end
|
81
95
|
|
82
96
|
if @reverse
|
83
|
-
|
84
|
-
status = Timeout::timeout(@timeout) {
|
85
|
-
reverse(new_event)
|
86
|
-
}
|
87
|
-
return if status.nil?
|
88
|
-
rescue Timeout::Error
|
89
|
-
@logger.debug("DNS: reverse action timed out")
|
90
|
-
return
|
91
|
-
end
|
97
|
+
return if reverse(event).nil?
|
92
98
|
end
|
93
99
|
|
94
|
-
filter_matched(
|
95
|
-
yield new_event
|
96
|
-
event.cancel
|
100
|
+
filter_matched(event)
|
97
101
|
end
|
98
102
|
|
99
103
|
private
|
@@ -111,25 +115,24 @@ class LogStash::Filters::DNS < LogStash::Filters::Base
|
|
111
115
|
end
|
112
116
|
|
113
117
|
begin
|
114
|
-
|
115
|
-
|
118
|
+
return if @failed_cache && @failed_cache[raw] # recently failed resolv, skip
|
119
|
+
if @hit_cache
|
120
|
+
address = @hit_cache.getset(raw) { retriable_getaddress(raw) }
|
121
|
+
else
|
122
|
+
address = retriable_getaddress(raw)
|
123
|
+
end
|
116
124
|
rescue Resolv::ResolvError
|
125
|
+
@failed_cache[raw] = true if @failed_cache
|
117
126
|
@logger.debug("DNS: couldn't resolve the hostname.",
|
118
127
|
:field => field, :value => raw)
|
119
128
|
return
|
120
|
-
rescue Resolv::ResolvTimeout
|
121
|
-
@logger.
|
129
|
+
rescue Resolv::ResolvTimeout, Timeout::Error
|
130
|
+
@logger.error("DNS: timeout on resolving the hostname.",
|
122
131
|
:field => field, :value => raw)
|
123
132
|
return
|
124
133
|
rescue SocketError => e
|
125
|
-
@logger.
|
126
|
-
:field => field, :value => raw)
|
127
|
-
return
|
128
|
-
rescue NoMethodError => e
|
129
|
-
# see JRUBY-5647
|
130
|
-
@logger.debug("DNS: couldn't resolve the hostname.",
|
131
|
-
:field => field, :value => raw,
|
132
|
-
:extra => "NameError instead of ResolvError")
|
134
|
+
@logger.error("DNS: Encountered SocketError.",
|
135
|
+
:field => field, :value => raw, :message => e.message)
|
133
136
|
return
|
134
137
|
end
|
135
138
|
|
@@ -170,19 +173,24 @@ class LogStash::Filters::DNS < LogStash::Filters::Base
|
|
170
173
|
return
|
171
174
|
end
|
172
175
|
begin
|
173
|
-
|
174
|
-
|
176
|
+
return if @failed_cache && @failed_cache.key?(raw) # recently failed resolv, skip
|
177
|
+
if @hit_cache
|
178
|
+
hostname = @hit_cache.getset(raw) { retriable_getname(raw) }
|
179
|
+
else
|
180
|
+
hostname = retriable_getname(raw)
|
181
|
+
end
|
175
182
|
rescue Resolv::ResolvError
|
183
|
+
@failed_cache[raw] = true if @failed_cache
|
176
184
|
@logger.debug("DNS: couldn't resolve the address.",
|
177
185
|
:field => field, :value => raw)
|
178
186
|
return
|
179
|
-
rescue Resolv::ResolvTimeout
|
180
|
-
@logger.
|
187
|
+
rescue Resolv::ResolvTimeout, Timeout::Error
|
188
|
+
@logger.error("DNS: timeout on resolving address.",
|
181
189
|
:field => field, :value => raw)
|
182
190
|
return
|
183
191
|
rescue SocketError => e
|
184
|
-
@logger.
|
185
|
-
:field => field, :value => raw)
|
192
|
+
@logger.error("DNS: Encountered SocketError.",
|
193
|
+
:field => field, :value => raw, :message => e.message)
|
186
194
|
return
|
187
195
|
end
|
188
196
|
|
@@ -201,4 +209,45 @@ class LogStash::Filters::DNS < LogStash::Filters::Base
|
|
201
209
|
end
|
202
210
|
end
|
203
211
|
end
|
212
|
+
|
213
|
+
private
|
214
|
+
def retriable_request(&block)
|
215
|
+
tries = 0
|
216
|
+
begin
|
217
|
+
Timeout::timeout(@timeout) do
|
218
|
+
block.call
|
219
|
+
end
|
220
|
+
rescue Timeout::Error, SocketError
|
221
|
+
if tries < @max_retries
|
222
|
+
tries = tries + 1
|
223
|
+
retry
|
224
|
+
else
|
225
|
+
raise
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
def retriable_getname(address)
|
232
|
+
retriable_request do
|
233
|
+
getname(address)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
def retriable_getaddress(name)
|
239
|
+
retriable_request do
|
240
|
+
getaddress(name)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
def getname(address)
|
246
|
+
@resolv.getname(address).force_encoding(Encoding::UTF_8)
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
def getaddress(name)
|
251
|
+
@resolv.getaddress(name).force_encoding(Encoding::UTF_8)
|
252
|
+
end
|
204
253
|
end # class LogStash::Filters::DNS
|
data/logstash-filter-dns.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
|
3
3
|
s.name = 'logstash-filter-dns'
|
4
|
-
s.version = '2.0
|
4
|
+
s.version = '2.1.0'
|
5
5
|
s.licenses = ['Apache License (2.0)']
|
6
6
|
s.summary = "This filter will resolve any IP addresses from a field of your choosing."
|
7
7
|
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"
|
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
|
22
22
|
# Gem dependencies
|
23
23
|
s.add_runtime_dependency "logstash-core", ">= 2.0.0.beta2", "< 3.0.0"
|
24
|
+
s.add_runtime_dependency 'lru_redux', "~> 1.1.0"
|
24
25
|
|
25
26
|
s.add_development_dependency 'logstash-devutils'
|
26
27
|
end
|
data/spec/filters/dns_spec.rb
CHANGED
@@ -89,7 +89,7 @@ describe LogStash::Filters::DNS do
|
|
89
89
|
|
90
90
|
sample("host1" => "carrera.databits.net", "host2" => "nonexistanthostname###.net") do
|
91
91
|
insist { subject["tags"] }.nil?
|
92
|
-
insist { subject["host1"] } == "
|
92
|
+
insist { subject["host1"] } == "199.192.228.250"
|
93
93
|
insist { subject["host2"] } == "nonexistanthostname###.net"
|
94
94
|
end
|
95
95
|
end
|
@@ -126,8 +126,8 @@ describe LogStash::Filters::DNS do
|
|
126
126
|
"ip1" => "127.0.0.1",
|
127
127
|
"ip2" => "128.0.0.1") do
|
128
128
|
insist { subject["tags"] }.nil?
|
129
|
-
insist { subject["host1"] } == "
|
130
|
-
insist { subject["ip1"] } == "
|
129
|
+
insist { subject["host1"] } == "199.192.228.250"
|
130
|
+
insist { subject["ip1"] } == "localhost"
|
131
131
|
insist { subject["ip2"] } == "128.0.0.1"
|
132
132
|
end
|
133
133
|
end
|
@@ -238,4 +238,124 @@ describe LogStash::Filters::DNS do
|
|
238
238
|
insist { subject["host"] } == "199.192.228.250"
|
239
239
|
end
|
240
240
|
end
|
241
|
+
|
242
|
+
describe "dns resolve lookup, multiple nameserver fallback" do
|
243
|
+
config <<-CONFIG
|
244
|
+
filter {
|
245
|
+
dns {
|
246
|
+
resolve => ["host"]
|
247
|
+
action => "replace"
|
248
|
+
nameserver => ["127.0.0.99", "8.8.8.8"]
|
249
|
+
}
|
250
|
+
}
|
251
|
+
CONFIG
|
252
|
+
|
253
|
+
sample("host" => "carrera.databits.net") do
|
254
|
+
insist { subject["host"] } == "199.192.228.250"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe "failed cache" do
|
259
|
+
|
260
|
+
let(:subject) { LogStash::Filters::DNS.new(config) }
|
261
|
+
let(:event1) { LogStash::Event.new("message" => "unkownhost") }
|
262
|
+
let(:event2) { LogStash::Event.new("message" => "unkownhost") }
|
263
|
+
|
264
|
+
before(:each) do
|
265
|
+
allow(subject).to receive(:getaddress).and_raise Resolv::ResolvError
|
266
|
+
subject.register
|
267
|
+
end
|
268
|
+
|
269
|
+
context "when enabled" do
|
270
|
+
let(:config) { { "resolve" => ["message"], "failed_cache_size" => 3 } }
|
271
|
+
|
272
|
+
it "should cache a failed lookup" do
|
273
|
+
expect(subject).to receive(:getaddress).once
|
274
|
+
subject.filter(event1)
|
275
|
+
subject.filter(event2)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
context "when disabled" do
|
280
|
+
let(:config) { { "resolve" => ["message"] } }
|
281
|
+
|
282
|
+
it "should not cache a failed lookup" do
|
283
|
+
expect(subject).to receive(:getaddress).twice
|
284
|
+
subject.filter(event1)
|
285
|
+
subject.filter(event2)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "hit cache" do
|
291
|
+
|
292
|
+
let(:subject) { LogStash::Filters::DNS.new(config) }
|
293
|
+
let(:event1) { LogStash::Event.new("message" => "unkownhost") }
|
294
|
+
let(:event2) { LogStash::Event.new("message" => "unkownhost") }
|
295
|
+
|
296
|
+
before(:each) do
|
297
|
+
allow(subject).to receive(:getaddress).and_return("127.0.0.1")
|
298
|
+
subject.register
|
299
|
+
end
|
300
|
+
|
301
|
+
context "when enabled" do
|
302
|
+
let(:config) { { "resolve" => ["message"], "hit_cache_size" => 3 } }
|
303
|
+
|
304
|
+
it "should cache a succesful lookup" do
|
305
|
+
expect(subject).to receive(:getaddress).once
|
306
|
+
subject.filter(event1)
|
307
|
+
subject.filter(event2)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context "when disabled" do
|
312
|
+
let(:config) { { "resolve" => ["message"] } }
|
313
|
+
|
314
|
+
it "should not cache a successful lookup" do
|
315
|
+
expect(subject).to receive(:getaddress).twice
|
316
|
+
subject.filter(event1)
|
317
|
+
subject.filter(event2)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe "retries" do
|
323
|
+
|
324
|
+
let(:subject) { LogStash::Filters::DNS.new(config) }
|
325
|
+
let(:event) { LogStash::Event.new("message" => "unkownhost") }
|
326
|
+
let(:max_retries) { 3 }
|
327
|
+
let(:config) { { "resolve" => ["message"], "max_retries" => max_retries } }
|
328
|
+
|
329
|
+
before(:each) { subject.register }
|
330
|
+
|
331
|
+
context "when failing permanently" do
|
332
|
+
before(:each) do
|
333
|
+
allow(subject).to receive(:getaddress).and_raise(Timeout::Error)
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should fail a resolve after max_retries" do
|
337
|
+
expect(subject).to receive(:getaddress).exactly(max_retries+1).times
|
338
|
+
subject.filter(event)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
context "when failing temporarily" do
|
343
|
+
before(:each) do
|
344
|
+
allow(subject).to receive(:getaddress) do
|
345
|
+
@try ||= 0
|
346
|
+
if @try < 3
|
347
|
+
@try = @try + 1
|
348
|
+
raise Timeout::Error
|
349
|
+
else
|
350
|
+
return "127.0.0.1"
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should resolve before max_retries" do
|
356
|
+
expect(subject).to receive(:getaddress).exactly(3).times
|
357
|
+
subject.filter(event)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
241
361
|
end
|
metadata
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-filter-dns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
|
14
|
+
name: logstash-core
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - '>='
|
17
18
|
- !ruby/object:Gem::Version
|
@@ -19,10 +20,7 @@ dependencies:
|
|
19
20
|
- - <
|
20
21
|
- !ruby/object:Gem::Version
|
21
22
|
version: 3.0.0
|
22
|
-
|
23
|
-
prerelease: false
|
24
|
-
type: :runtime
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirement: !ruby/object:Gem::Requirement
|
26
24
|
requirements:
|
27
25
|
- - '>='
|
28
26
|
- !ruby/object:Gem::Version
|
@@ -30,20 +28,36 @@ dependencies:
|
|
30
28
|
- - <
|
31
29
|
- !ruby/object:Gem::Version
|
32
30
|
version: 3.0.0
|
31
|
+
prerelease: false
|
32
|
+
type: :runtime
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
+
name: lru_redux
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.1.0
|
34
40
|
requirement: !ruby/object:Gem::Requirement
|
35
41
|
requirements:
|
36
|
-
- -
|
42
|
+
- - ~>
|
37
43
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
39
|
-
name: logstash-devutils
|
44
|
+
version: 1.1.0
|
40
45
|
prerelease: false
|
41
|
-
type: :
|
46
|
+
type: :runtime
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: logstash-devutils
|
42
49
|
version_requirements: !ruby/object:Gem::Requirement
|
43
50
|
requirements:
|
44
51
|
- - '>='
|
45
52
|
- !ruby/object:Gem::Version
|
46
53
|
version: '0'
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
prerelease: false
|
60
|
+
type: :development
|
47
61
|
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
|
48
62
|
email: info@elastic.co
|
49
63
|
executables: []
|