logstash-core 2.2.4.snapshot1

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.

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