logstash-core 2.2.4.snapshot1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of logstash-core might be problematic. Click here for more details.

Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/lib/logstash-core.rb +1 -0
  3. data/lib/logstash-core/logstash-core.rb +3 -0
  4. data/lib/logstash-core/version.rb +8 -0
  5. data/lib/logstash/agent.rb +391 -0
  6. data/lib/logstash/codecs/base.rb +50 -0
  7. data/lib/logstash/config/config_ast.rb +550 -0
  8. data/lib/logstash/config/cpu_core_strategy.rb +32 -0
  9. data/lib/logstash/config/defaults.rb +12 -0
  10. data/lib/logstash/config/file.rb +39 -0
  11. data/lib/logstash/config/grammar.rb +3503 -0
  12. data/lib/logstash/config/mixin.rb +518 -0
  13. data/lib/logstash/config/registry.rb +13 -0
  14. data/lib/logstash/environment.rb +98 -0
  15. data/lib/logstash/errors.rb +12 -0
  16. data/lib/logstash/filters/base.rb +205 -0
  17. data/lib/logstash/inputs/base.rb +116 -0
  18. data/lib/logstash/inputs/threadable.rb +18 -0
  19. data/lib/logstash/java_integration.rb +116 -0
  20. data/lib/logstash/json.rb +61 -0
  21. data/lib/logstash/logging.rb +91 -0
  22. data/lib/logstash/namespace.rb +13 -0
  23. data/lib/logstash/output_delegator.rb +172 -0
  24. data/lib/logstash/outputs/base.rb +91 -0
  25. data/lib/logstash/patches.rb +5 -0
  26. data/lib/logstash/patches/bugfix_jruby_2558.rb +51 -0
  27. data/lib/logstash/patches/cabin.rb +35 -0
  28. data/lib/logstash/patches/profile_require_calls.rb +47 -0
  29. data/lib/logstash/patches/rubygems.rb +38 -0
  30. data/lib/logstash/patches/stronger_openssl_defaults.rb +68 -0
  31. data/lib/logstash/pipeline.rb +499 -0
  32. data/lib/logstash/pipeline_reporter.rb +114 -0
  33. data/lib/logstash/plugin.rb +120 -0
  34. data/lib/logstash/program.rb +14 -0
  35. data/lib/logstash/runner.rb +124 -0
  36. data/lib/logstash/shutdown_watcher.rb +100 -0
  37. data/lib/logstash/util.rb +203 -0
  38. data/lib/logstash/util/buftok.rb +139 -0
  39. data/lib/logstash/util/charset.rb +35 -0
  40. data/lib/logstash/util/decorators.rb +52 -0
  41. data/lib/logstash/util/defaults_printer.rb +31 -0
  42. data/lib/logstash/util/filetools.rb +186 -0
  43. data/lib/logstash/util/java_version.rb +66 -0
  44. data/lib/logstash/util/password.rb +25 -0
  45. data/lib/logstash/util/plugin_version.rb +56 -0
  46. data/lib/logstash/util/prctl.rb +10 -0
  47. data/lib/logstash/util/retryable.rb +40 -0
  48. data/lib/logstash/util/socket_peer.rb +7 -0
  49. data/lib/logstash/util/unicode_trimmer.rb +81 -0
  50. data/lib/logstash/util/worker_threads_default_printer.rb +29 -0
  51. data/lib/logstash/util/wrapped_synchronous_queue.rb +41 -0
  52. data/lib/logstash/version.rb +14 -0
  53. data/locales/en.yml +204 -0
  54. data/logstash-core.gemspec +58 -0
  55. data/spec/conditionals_spec.rb +429 -0
  56. data/spec/logstash/agent_spec.rb +85 -0
  57. data/spec/logstash/config/config_ast_spec.rb +146 -0
  58. data/spec/logstash/config/cpu_core_strategy_spec.rb +123 -0
  59. data/spec/logstash/config/defaults_spec.rb +10 -0
  60. data/spec/logstash/config/mixin_spec.rb +158 -0
  61. data/spec/logstash/environment_spec.rb +56 -0
  62. data/spec/logstash/filters/base_spec.rb +251 -0
  63. data/spec/logstash/inputs/base_spec.rb +74 -0
  64. data/spec/logstash/java_integration_spec.rb +304 -0
  65. data/spec/logstash/json_spec.rb +96 -0
  66. data/spec/logstash/output_delegator_spec.rb +144 -0
  67. data/spec/logstash/outputs/base_spec.rb +40 -0
  68. data/spec/logstash/patches_spec.rb +90 -0
  69. data/spec/logstash/pipeline_reporter_spec.rb +85 -0
  70. data/spec/logstash/pipeline_spec.rb +455 -0
  71. data/spec/logstash/plugin_spec.rb +169 -0
  72. data/spec/logstash/runner_spec.rb +68 -0
  73. data/spec/logstash/shutdown_watcher_spec.rb +113 -0
  74. data/spec/logstash/util/buftok_spec.rb +31 -0
  75. data/spec/logstash/util/charset_spec.rb +74 -0
  76. data/spec/logstash/util/defaults_printer_spec.rb +50 -0
  77. data/spec/logstash/util/java_version_spec.rb +79 -0
  78. data/spec/logstash/util/plugin_version_spec.rb +64 -0
  79. data/spec/logstash/util/unicode_trimmer_spec.rb +55 -0
  80. data/spec/logstash/util/worker_threads_default_printer_spec.rb +45 -0
  81. data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +28 -0
  82. data/spec/logstash/util_spec.rb +35 -0
  83. metadata +364 -0
@@ -0,0 +1,186 @@
1
+ # encoding: utf-8
2
+ require "net/http"
3
+ require "uri"
4
+ require "digest/sha1"
5
+ require 'fileutils'
6
+
7
+ module LogStash::Util::FileTools
8
+ extend self
9
+
10
+ def fetch(url, sha1, output)
11
+
12
+ puts "Downloading #{url}"
13
+ actual_sha1 = download(url, output)
14
+
15
+ if actual_sha1 != sha1
16
+ fail "SHA1 does not match (expected '#{sha1}' but got '#{actual_sha1}')"
17
+ end
18
+ end # def fetch
19
+
20
+ def file_fetch(url, sha1, target)
21
+ filename = File.basename( URI(url).path )
22
+ output = "#{target}/#{filename}"
23
+ begin
24
+ actual_sha1 = file_sha1(output)
25
+ if actual_sha1 != sha1
26
+ fetch(url, sha1, output)
27
+ end
28
+ rescue Errno::ENOENT
29
+ fetch(url, sha1, output)
30
+ end
31
+ return output
32
+ end
33
+
34
+ def file_sha1(path)
35
+ digest = Digest::SHA1.new
36
+ fd = File.new(path, "r")
37
+ while true
38
+ begin
39
+ digest << fd.sysread(16384)
40
+ rescue EOFError
41
+ break
42
+ end
43
+ end
44
+ return digest.hexdigest
45
+ ensure
46
+ fd.close if fd
47
+ end
48
+
49
+ def download(url, output)
50
+ uri = URI(url)
51
+ digest = Digest::SHA1.new
52
+ tmp = "#{output}.tmp"
53
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => (uri.scheme == "https")) do |http|
54
+ request = Net::HTTP::Get.new(uri.path)
55
+ http.request(request) do |response|
56
+ fail "HTTP fetch failed for #{url}. #{response}" if [200, 301].include?(response.code)
57
+ size = (response["content-length"].to_i || -1).to_f
58
+ count = 0
59
+ File.open(tmp, "w") do |fd|
60
+ response.read_body do |chunk|
61
+ fd.write(chunk)
62
+ digest << chunk
63
+ if size > 0 && $stdout.tty?
64
+ count += chunk.bytesize
65
+ $stdout.write(sprintf("\r%0.2f%%", count/size * 100))
66
+ end
67
+ end
68
+ end
69
+ $stdout.write("\r \r") if $stdout.tty?
70
+ end
71
+ end
72
+
73
+ File.rename(tmp, output)
74
+
75
+ return digest.hexdigest
76
+ rescue SocketError => e
77
+ puts "Failure while downloading #{url}: #{e}"
78
+ raise
79
+ ensure
80
+ File.unlink(tmp) if File.exist?(tmp)
81
+ end # def download
82
+
83
+ def untar(tarball, &block)
84
+ require "archive/tar/minitar"
85
+ tgz = Zlib::GzipReader.new(File.open(tarball))
86
+ # Pull out typesdb
87
+ tar = Archive::Tar::Minitar::Input.open(tgz)
88
+ tar.each do |entry|
89
+ path = block.call(entry)
90
+ next if path.nil?
91
+ parent = File.dirname(path)
92
+
93
+ FileUtils.mkdir_p(parent) unless File.directory?(parent)
94
+
95
+ # Skip this file if the output file is the same size
96
+ if entry.directory?
97
+ FileUtils.mkdir_p(path) unless File.directory?(path)
98
+ else
99
+ entry_mode = entry.instance_eval { @mode } & 0777
100
+ if File.exists?(path)
101
+ stat = File.stat(path)
102
+ # TODO(sissel): Submit a patch to archive-tar-minitar upstream to
103
+ # expose headers in the entry.
104
+ entry_size = entry.instance_eval { @size }
105
+ # If file sizes are same, skip writing.
106
+ next if stat.size == entry_size && (stat.mode & 0777) == entry_mode
107
+ end
108
+ puts "Extracting #{entry.full_name} from #{tarball} #{entry_mode.to_s(8)}"
109
+ File.open(path, "w") do |fd|
110
+ # eof? check lets us skip empty files. Necessary because the API provided by
111
+ # Archive::Tar::Minitar::Reader::EntryStream only mostly acts like an
112
+ # IO object. Something about empty files in this EntryStream causes
113
+ # IO.copy_stream to throw "can't convert nil into String" on JRuby
114
+ # TODO(sissel): File a bug about this.
115
+ while !entry.eof?
116
+ chunk = entry.read(16384)
117
+ fd.write(chunk)
118
+ end
119
+ #IO.copy_stream(entry, fd)
120
+ end
121
+ File.chmod(entry_mode, path)
122
+ end
123
+ end
124
+ tar.close
125
+ File.unlink(tarball) if File.file?(tarball)
126
+ end # def untar
127
+
128
+ def do_ungz(file)
129
+
130
+ outpath = file.gsub('.gz', '')
131
+ tgz = Zlib::GzipReader.new(File.open(file))
132
+ begin
133
+ File.open(outpath, "w") do |out|
134
+ IO::copy_stream(tgz, out)
135
+ end
136
+ File.unlink(file)
137
+ rescue
138
+ File.unlink(outpath) if File.file?(outpath)
139
+ raise
140
+ end
141
+ tgz.close
142
+ end
143
+
144
+ def eval_file(entry, files, prefix)
145
+ return false if entry.full_name =~ /PaxHeaders/
146
+ if !files.nil?
147
+ if files.is_a?(Array)
148
+ return false unless files.include?(entry.full_name.gsub(prefix, ''))
149
+ entry.full_name.split("/").last
150
+ elsif files.is_a?(String)
151
+ return false unless entry.full_name =~ Regexp.new(files)
152
+ entry.full_name.split("/").last
153
+ end
154
+ else
155
+ entry.full_name.gsub(prefix, '')
156
+ end
157
+ end
158
+
159
+ def process_downloads(files,target='')
160
+
161
+ FileUtils.mkdir_p(target) unless File.directory?(target)
162
+
163
+ files.each do |file|
164
+ download = file_fetch(file['url'], file['sha1'],target)
165
+
166
+ if download =~ /.tar.gz/
167
+ prefix = download.gsub('.tar.gz', '').gsub("#{target}/", '')
168
+ untar(download) do |entry|
169
+ next unless out = eval_file(entry, file['files'], prefix)
170
+ File.join(target, out)
171
+ end
172
+
173
+ elsif download =~ /.tgz/
174
+ prefix = download.gsub('.tgz', '').gsub("#{target}/", '')
175
+ untar(download) do |entry|
176
+ next unless out = eval_file(entry, file['files'], prefix)
177
+ File.join(target, out)
178
+ end
179
+
180
+ elsif download =~ /.gz/
181
+ do_ungz(download)
182
+ end
183
+ end
184
+ end
185
+
186
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ require 'cabin'
3
+
4
+ module LogStash::Util::JavaVersion
5
+ def self.logger
6
+ @logger ||= Cabin::Channel.get(LogStash)
7
+ end
8
+
9
+ # Print a warning if we're on a bad version of java
10
+ def self.warn_on_bad_java_version
11
+ if self.bad_java_version?(self.version)
12
+ msg = "!!! Please upgrade your java version, the current version '#{self.version}' may cause problems. We recommend a minimum version of 1.7.0_51"
13
+ STDERR.puts(msg)
14
+ logger.warn(msg)
15
+ end
16
+ end
17
+
18
+ # Return the current java version string. Returns nil if this is a non-java platform (e.g. MRI).
19
+ def self.version
20
+ return nil unless LogStash::Environment.jruby?
21
+ java.lang.System.getProperty("java.runtime.version")
22
+ end
23
+
24
+ # Takes a string of a java version ex: "1.8.0_24-beta"
25
+ # and returns a parsed map of the components.
26
+ # nil inputs will be returned as nil.
27
+ def self.parse_java_version(version_string)
28
+ return nil if version_string.nil?
29
+
30
+ # Crazy java versioning rules @ http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
31
+ # The regex below parses this all correctly http://rubular.com/r/sInQc3Nc7f
32
+
33
+ match = version_string.match(/\A(\d+)\.(\d+)\.(\d+)(_(\d+))?(-(.+))?\Z/)
34
+
35
+ return nil unless match
36
+
37
+ major, minor, patch, ufull, update, bfull, build = match.captures
38
+
39
+ {
40
+ :full => version_string,
41
+ :major => major.to_i,
42
+ :minor => minor.to_i,
43
+ :patch => patch.to_i,
44
+ :update => update.to_i, # this is always coerced to an int (a nil will be zero) to make comparisons easier
45
+ :build => build # not an integer, could be b06 for instance!,
46
+ }
47
+ end
48
+
49
+ # Determine if the given java version string is a bad version of java
50
+ # If it is, return true, if it isn't return false.
51
+ # Accepts nil, returning nil.
52
+ def self.bad_java_version?(version_string)
53
+ return nil if version_string.nil?
54
+
55
+ parsed = parse_java_version(version_string)
56
+ return false unless parsed
57
+
58
+ if parsed[:major] == 1 && parsed[:minor] == 7 && parsed[:patch] == 0 && parsed[:update] < 51
59
+ true
60
+ elsif parsed[:major] == 1 && parsed[:minor] < 7
61
+ true
62
+ else
63
+ false
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/util"
4
+
5
+ # This class exists to quietly wrap a password string so that, when printed or
6
+ # logged, you don't accidentally print the password itself.
7
+ class LogStash::Util::Password
8
+ attr_reader :value
9
+
10
+ public
11
+ def initialize(password)
12
+ @value = password
13
+ end # def initialize
14
+
15
+ public
16
+ def to_s
17
+ return "<password>"
18
+ end # def to_s
19
+
20
+ public
21
+ def inspect
22
+ return to_s
23
+ end # def inspect
24
+ end # class LogStash::Util::Password
25
+
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ require 'logstash/errors'
3
+ require 'rubygems/version'
4
+ require 'forwardable'
5
+
6
+ module LogStash::Util
7
+ class PluginVersion
8
+ extend Forwardable
9
+ include Comparable
10
+
11
+ GEM_NAME_PREFIX = 'logstash'
12
+
13
+ def_delegators :@version, :to_s
14
+ attr_reader :version
15
+
16
+ def initialize(*options)
17
+ if options.size == 1 && options.first.is_a?(Gem::Version)
18
+ @version = options.first
19
+ else
20
+ @version = Gem::Version.new(options.join('.'))
21
+ end
22
+ end
23
+
24
+ def self.find_version!(name)
25
+ begin
26
+ spec = Gem::Specification.find_by_name(name)
27
+ if spec.nil?
28
+ # Checking for nil? is a workaround for situations where find_by_name
29
+ # is not able to find the real spec, as for example with pre releases
30
+ # of plugins
31
+ spec = Gem::Specification.find_all_by_name(name).first
32
+ end
33
+ new(spec.version)
34
+ rescue Gem::LoadError
35
+ # Rescuing the LoadError and raise a Logstash specific error.
36
+ # Likely we can't find the gem in the current GEM_PATH
37
+ raise LogStash::PluginNoVersionError
38
+ end
39
+ end
40
+
41
+ def self.find_plugin_version!(type, name)
42
+ plugin_name = [GEM_NAME_PREFIX, type, name].join('-')
43
+ find_version!(plugin_name)
44
+ end
45
+
46
+ def <=>(other)
47
+ version <=> other.version
48
+ end
49
+
50
+ private
51
+
52
+ def self.build_from_spec(spec)
53
+ new(spec.version)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+ module LibC
3
+ require "ffi"
4
+ extend FFI::Library
5
+ ffi_lib 'c'
6
+
7
+ # Ok so the 2nd arg isn't really a string... but whaatever
8
+ attach_function :prctl, [:int, :string, :long, :long, :long], :int
9
+ end
10
+
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ module LogStash
3
+ module Retryable
4
+ # execute retryable code block
5
+ # @param [Hash] options retryable options
6
+ # @option options [Fixnum] :tries retries to perform, default 1, set to 0 for infite retries. 1 means that upon exception the block will be retried once
7
+ # @option options [Fixnum] :base_sleep seconds to sleep on first retry, default 1
8
+ # @option options [Fixnum] :max_sleep max seconds to sleep upon exponential backoff, default 1
9
+ # @option options [Exception] :rescue exception class list to retry on, defaults is Exception, which retries on any Exception.
10
+ # @option options [Proc] :on_retry call the given Proc/lambda before each retry with the raised exception as parameter
11
+ def retryable(options = {}, &block)
12
+ options = {
13
+ :tries => 1,
14
+ :rescue => Exception,
15
+ :on_retry => nil,
16
+ :base_sleep => 1,
17
+ :max_sleep => 1,
18
+ }.merge(options)
19
+
20
+ rescue_classes = Array(options[:rescue])
21
+ max_sleep_retry = Math.log2(options[:max_sleep] / options[:base_sleep])
22
+ retry_count = 0
23
+
24
+ begin
25
+ return yield(retry_count)
26
+ rescue *rescue_classes => e
27
+ raise e if options[:tries] > 0 && retry_count >= options[:tries]
28
+
29
+ options[:on_retry].call(retry_count + 1, e) if options[:on_retry]
30
+
31
+ # dont compute and maybe overflow exponent on too big a retry count
32
+ seconds = retry_count < max_sleep_retry ? options[:base_sleep] * (2 ** retry_count) : options[:max_sleep]
33
+ sleep(seconds)
34
+
35
+ retry_count += 1
36
+ retry
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+ module ::LogStash::Util::SocketPeer
3
+ public
4
+ def peer
5
+ "#{peeraddr[3]}:#{peeraddr[1]}"
6
+ end # def peer
7
+ end # module SocketPeer
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ module LogStash::Util::UnicodeTrimmer
3
+ # The largest possible unicode chars are 4 bytes
4
+ # http://stackoverflow.com/questions/9533258/what-is-the-maximum-number-of-bytes-for-a-utf-8-encoded-character
5
+ # http://tools.ietf.org/html/rfc3629
6
+ MAX_CHAR_BYTES = 4
7
+
8
+ # Takes a unicode string and makes sure it fits in a max of `desired_bytes`
9
+ # This aims to be somewhat efficient about this for the average case and get as close to
10
+ # O(1) as possible. Given certain distributions of multi-byte characters it'll be slower
11
+ # It tries to find the point the truncation *should* happen based on the average byte size.
12
+ # If that snips it in the wrong place it'll try to add or remove chars to get it to the right
13
+ # spot and preserve as much data as possible.
14
+ public
15
+ def self.trim_bytes(orig_str, desired_bytes)
16
+ return orig_str if orig_str.bytesize <= desired_bytes
17
+
18
+ pre_shortened = pre_shorten(orig_str, desired_bytes)
19
+
20
+ case pre_shortened.bytesize <=> desired_bytes
21
+ when 0
22
+ pre_shortened
23
+ when 1
24
+ shrink_bytes(pre_shortened, orig_str, desired_bytes)
25
+ when -1
26
+ grow_bytes(pre_shortened, orig_str, desired_bytes)
27
+ end
28
+ end
29
+
30
+ private
31
+ # Try to cut the string at the right place based on the avg. byte size
32
+ def self.pre_shorten(orig_str, desired_bytes)
33
+ # Compute the average size to get an idea of where should chop
34
+ orig_len = orig_str.length
35
+ orig_bs = orig_str.bytesize
36
+ avg_size = (orig_bs.to_f / orig_len.to_f)
37
+
38
+ # Try to do an initial shortening based on the average char size
39
+ # The goal here is to get us somewhere above or below the boundary quickly
40
+ orig_extra_bytes = orig_bs - desired_bytes
41
+ pre_shorten_by = (orig_extra_bytes / avg_size).to_i
42
+ orig_str.slice(0, orig_len - pre_shorten_by)
43
+ end
44
+
45
+ private
46
+ def self.grow_bytes(pre_shortened, orig_str, desired_bytes)
47
+ res_str = pre_shortened.clone()
48
+
49
+ loop do
50
+ bs = res_str.bytesize
51
+ deficit = desired_bytes - bs
52
+ lengthen_by = deficit / MAX_CHAR_BYTES
53
+ lengthen_by = 1 if lengthen_by < 1
54
+ append = orig_str.slice(res_str.length, lengthen_by)
55
+
56
+ break if (bs + append.bytesize) > desired_bytes
57
+
58
+ res_str << append
59
+ end
60
+
61
+ res_str
62
+ end
63
+
64
+ private
65
+ def self.shrink_bytes(pre_shortened, orig_str, desired_bytes)
66
+ res_str = pre_shortened.clone()
67
+
68
+ loop do
69
+ bs = res_str.bytesize
70
+ break if bs <= desired_bytes
71
+
72
+ extra = bs - desired_bytes
73
+ shorten_by = extra / MAX_CHAR_BYTES
74
+ shorten_by = 1 if shorten_by < 1
75
+
76
+ res_str.slice!(res_str.length - shorten_by)
77
+ end
78
+
79
+ res_str
80
+ end
81
+ end