logstash-output-oss 0.1.1-java

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2 -0
  3. data/CONTRIBUTORS +10 -0
  4. data/DEVELOPER.md +10 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +11 -0
  7. data/README.md +149 -0
  8. data/lib/com/aliyun/aliyun-java-sdk-core/3.4.0/aliyun-java-sdk-core-3.4.0.jar +0 -0
  9. data/lib/com/aliyun/aliyun-java-sdk-ecs/4.2.0/aliyun-java-sdk-ecs-4.2.0.jar +0 -0
  10. data/lib/com/aliyun/aliyun-java-sdk-ram/3.0.0/aliyun-java-sdk-ram-3.0.0.jar +0 -0
  11. data/lib/com/aliyun/aliyun-java-sdk-sts/3.0.0/aliyun-java-sdk-sts-3.0.0.jar +0 -0
  12. data/lib/com/aliyun/oss/aliyun-sdk-oss/3.4.0/aliyun-sdk-oss-3.4.0.jar +0 -0
  13. data/lib/com/sun/jersey/jersey-core/1.9/jersey-core-1.9.jar +0 -0
  14. data/lib/com/sun/jersey/jersey-json/1.9/jersey-json-1.9.jar +0 -0
  15. data/lib/com/sun/xml/bind/jaxb-impl/2.2.3-1/jaxb-impl-2.2.3-1.jar +0 -0
  16. data/lib/commons-codec/commons-codec/1.9/commons-codec-1.9.jar +0 -0
  17. data/lib/commons-logging/commons-logging/1.2/commons-logging-1.2.jar +0 -0
  18. data/lib/javax/activation/activation/1.1/activation-1.1.jar +0 -0
  19. data/lib/javax/xml/bind/jaxb-api/2.2.2/jaxb-api-2.2.2.jar +0 -0
  20. data/lib/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar +0 -0
  21. data/lib/logstash-output-oss_jars.rb +52 -0
  22. data/lib/logstash/outputs/oss.rb +288 -0
  23. data/lib/logstash/outputs/oss/file_generator.rb +69 -0
  24. data/lib/logstash/outputs/oss/file_manager.rb +87 -0
  25. data/lib/logstash/outputs/oss/file_uploader.rb +69 -0
  26. data/lib/logstash/outputs/oss/gzip_file.rb +36 -0
  27. data/lib/logstash/outputs/oss/rotations/hybrid_rotation.rb +24 -0
  28. data/lib/logstash/outputs/oss/rotations/size_based_rotation.rb +25 -0
  29. data/lib/logstash/outputs/oss/rotations/time_based_rotation.rb +25 -0
  30. data/lib/logstash/outputs/oss/temporary_file.rb +59 -0
  31. data/lib/logstash/outputs/oss/version.rb +14 -0
  32. data/lib/org/apache/httpcomponents/httpclient/4.4.1/httpclient-4.4.1.jar +0 -0
  33. data/lib/org/apache/httpcomponents/httpcore/4.4.1/httpcore-4.4.1.jar +0 -0
  34. data/lib/org/codehaus/jackson/jackson-core-asl/1.8.3/jackson-core-asl-1.8.3.jar +0 -0
  35. data/lib/org/codehaus/jackson/jackson-jaxrs/1.8.3/jackson-jaxrs-1.8.3.jar +0 -0
  36. data/lib/org/codehaus/jackson/jackson-mapper-asl/1.8.3/jackson-mapper-asl-1.8.3.jar +0 -0
  37. data/lib/org/codehaus/jackson/jackson-xc/1.8.3/jackson-xc-1.8.3.jar +0 -0
  38. data/lib/org/codehaus/jettison/jettison/1.1/jettison-1.1.jar +0 -0
  39. data/lib/org/jdom/jdom/1.1/jdom-1.1.jar +0 -0
  40. data/lib/stax/stax-api/1.0.1/stax-api-1.0.1.jar +0 -0
  41. data/logstash-output-oss.gemspec +30 -0
  42. data/spec/integration/common.rb +42 -0
  43. data/spec/integration/encoding_spec.rb +92 -0
  44. data/spec/integration/oss_spec.rb +47 -0
  45. data/spec/integration/recover_spec.rb +43 -0
  46. data/spec/outputs/oss/generator_spec.rb +67 -0
  47. data/spec/outputs/oss/rotation_spec.rb +69 -0
  48. data/spec/outputs/oss_spec.rb +58 -0
  49. metadata +206 -0
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ require 'uuid'
4
+ require 'logstash/outputs/oss/gzip_file'
5
+ require 'logstash/outputs/oss/temporary_file'
6
+
7
+ module LogStash
8
+ module Outputs
9
+ class OSS
10
+ class FileGenerator
11
+ FILE_MODE = "a"
12
+ STRFTIME = "%Y-%m-%dT%H.%M"
13
+
14
+ attr_accessor :index, :prefix, :encoding, :temporary_directory, :current_file
15
+
16
+ # `prefix`/logstash.oss.{random-uuid}.{%Y-%m-%dT%H.%M}.part-{index}.{extension}
17
+ def initialize(prefix, encoding, temporary_directory)
18
+ @index = 0
19
+ @prefix = prefix
20
+ # gzip or plain
21
+ @encoding = encoding
22
+ # temporary directory to save temporary file before upload to OSS
23
+ @temporary_directory = temporary_directory
24
+
25
+ @lock = Mutex.new
26
+
27
+ rotate
28
+ end
29
+
30
+ def rotate
31
+ @current_file = create_file
32
+ @index += 1
33
+ @current_file
34
+ end
35
+
36
+ def with_lock
37
+ @lock.synchronize do
38
+ yield self
39
+ end
40
+ end
41
+
42
+ def extension
43
+ @encoding == "gzip" ? "gz" : "data"
44
+ end
45
+
46
+ def gzip?
47
+ @encoding == "gzip"
48
+ end
49
+
50
+ private
51
+ def create_file
52
+ uuid = UUID.new.generate
53
+ file_name = "ls.oss.#{uuid}.#{Time.now.strftime(STRFTIME)}.part-#{index}.#{extension}"
54
+ object_key = ::File.join(prefix, file_name)
55
+ local_path = ::File.join(temporary_directory, uuid)
56
+
57
+ FileUtils.mkdir_p(::File.join(local_path, prefix))
58
+ file = if gzip?
59
+ GzipFile.new(::File.open(::File.join(local_path, object_key), FILE_MODE))
60
+ else
61
+ ::File.open(::File.join(local_path, object_key), FILE_MODE)
62
+ end
63
+
64
+ TemporaryFile.new(file, object_key, local_path)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+
3
+ require "java"
4
+ require "concurrent"
5
+ require "concurrent/timer_task"
6
+ require "logstash/util"
7
+ require "logstash/outputs/oss/file_generator"
8
+
9
+ ConcurrentHashMap = java.util.concurrent.ConcurrentHashMap
10
+
11
+ module LogStash
12
+ module Outputs
13
+ class OSS
14
+ class FileManager
15
+
16
+ STALE_FILES_CHECK_INTERVAL_IN_SECONDS = 15 * 60
17
+
18
+ def initialize(logger, encoding, temporary_directory)
19
+ @logger = logger
20
+ @encoding = encoding
21
+
22
+ @temporary_directory = temporary_directory
23
+ # map of generators
24
+ # since `prefix` support string interpolation, so we will write many files at the same time
25
+ @prefixed_generators = ConcurrentHashMap.new
26
+
27
+ @file_generator_initialize = FileGeneratorInitializer.new(encoding, temporary_directory)
28
+
29
+ start_stale_files_check
30
+ end
31
+
32
+ def get_file_generator(prefix)
33
+ @prefixed_generators.computeIfAbsent(prefix, @file_generator_initialize).with_lock {|generator| yield generator}
34
+ end
35
+
36
+ def prefixes
37
+ @prefixed_generators.keySet
38
+ end
39
+
40
+ private
41
+ def remove_stale_files
42
+ prefixes.each do |prefix|
43
+ get_file_generator(prefix) do |file_generator|
44
+ file = file_generator.current_file
45
+ if file.size == 0 && Time.now - file.ctime > STALE_FILES_CHECK_INTERVAL_IN_SECONDS
46
+ @logger.info("Logstash OSS Output Plugin starts to remove stale file",
47
+ :key => file.key,
48
+ :path => file.path,
49
+ :size => file.size,
50
+ :thread => Thread.current.to_s)
51
+ @prefixed_generators.remove(prefix)
52
+ file.delete!
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+ def start_stale_files_check
60
+ @stale_check = Concurrent::TimerTask.new(:execution_interval => STALE_FILES_CHECK_INTERVAL_IN_SECONDS) do
61
+ @logger.info("Logstash OSS Output Plugin: start to check stale files")
62
+ remove_stale_files
63
+ end
64
+
65
+ @stale_check.execute
66
+ end
67
+
68
+ public
69
+ def close
70
+ @stale_check.shutdown
71
+ end
72
+
73
+ class FileGeneratorInitializer
74
+ include java.util.function.Function
75
+ def initialize(encoding, temporary_directory)
76
+ @encoding = encoding
77
+ @temporary_directory = temporary_directory
78
+ end
79
+
80
+ def apply(prefix)
81
+ FileGenerator.new(prefix, @encoding, @temporary_directory)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+ require 'java'
3
+ java_import 'com.aliyun.oss.model.ObjectMetadata'
4
+ java_import 'java.io.FileInputStream'
5
+
6
+ module LogStash
7
+ module Outputs
8
+ class OSS
9
+ class FileUploader
10
+ TIME_BEFORE_RETRY_SECONDS = 3
11
+
12
+ attr_reader :oss, :bucket, :additional_oss_settings, :logger
13
+
14
+ def initialize(oss, bucket, additional_oss_settings, logger, thread_pool)
15
+ @oss = oss
16
+ @bucket = bucket
17
+ @additional_oss_settings = additional_oss_settings
18
+ @logger = logger
19
+ @thread_pool = thread_pool
20
+ end
21
+
22
+ def upload_async(file, options = {})
23
+ @thread_pool.post do
24
+ LogStash::Util.set_thread_name("Logstash OSS Output Plugin: output uploader, file: #{file.path}")
25
+ upload(file, options)
26
+ end
27
+ end
28
+
29
+ def upload(file, options = {})
30
+ meta = ObjectMetadata.new
31
+ meta.setContentLength(file.size)
32
+ unless @additional_oss_settings.nil?
33
+ if @additional_oss_settings.include?(LogStash::Outputs::OSS::SERVER_SIDE_ENCRYPTION_ALGORITHM_KEY)
34
+ unless @additional_oss_settings[LogStash::Outputs::OSS::SERVER_SIDE_ENCRYPTION_ALGORITHM_KEY].empty?
35
+ meta.setServerSideEncryption(@additional_oss_settings[LogStash::Outputs::OSS::SERVER_SIDE_ENCRYPTION_ALGORITHM_KEY])
36
+ end
37
+ end
38
+ end
39
+
40
+ stream = nil
41
+ begin
42
+ stream = FileInputStream.new(file.path)
43
+ oss.putObject(@bucket, file.key, stream, meta)
44
+ rescue Errno::ENOENT => e
45
+ logger.error("Logstash OSS Output Plugin: file to be uploaded doesn't exist!", :exception => e.class, :message => e.message, :path => file.path, :backtrace => e.backtrace)
46
+ rescue => e
47
+ @logger.error("Logstash OSS Output Plugin: uploading failed, retrying.", :exception => e.class, :message => e.message, :path => file.path, :backtrace => e.backtrace)
48
+ sleep TIME_BEFORE_RETRY_SECONDS
49
+ retry
50
+ ensure
51
+ unless stream.nil?
52
+ stream.close
53
+ end
54
+ end
55
+
56
+ options[:on_complete].call(file) unless options[:on_complete].nil?
57
+ rescue => e
58
+ logger.error("Logstash OSS Output Plugin: an error occurred in the `on_complete` uploader", :exception => e.class, :message => e.message, :path => file.path, :backtrace => e.backtrace)
59
+ raise e
60
+ end
61
+
62
+ def close
63
+ @thread_pool.shutdown
64
+ @thread_pool.wait_for_termination(nil)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ module LogStash
4
+ module Outputs
5
+ class OSS
6
+ class GzipFile
7
+ extend Forwardable
8
+
9
+ def_delegators :@gzip_writer, :write, :close
10
+ attr_reader :file, :gzip_writer
11
+
12
+ def initialize(file)
13
+ @file = file
14
+ @gzip_writer = Zlib::GzipWriter.new(file)
15
+ end
16
+
17
+ def path
18
+ @gzip_writer.to_io.path
19
+ end
20
+
21
+ def size
22
+ if @gzip_writer.pos == 0
23
+ 0
24
+ else
25
+ @gzip_writer.flush
26
+ @gzip_writer.to_io.size
27
+ end
28
+ end
29
+
30
+ def fsync
31
+ @gzip_writer.to_io.fsync
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ require "logstash/outputs/oss/rotations/size_based_rotation"
3
+ require "logstash/outputs/oss/rotations/time_based_rotation"
4
+
5
+ module LogStash
6
+ module Outputs
7
+ class OSS
8
+ class HybridRotation
9
+ def initialize(size_rotate, time_rotate)
10
+ @size_strategy = SizeBasedRotation.new(size_rotate)
11
+ @time_strategy = TimeBasedRotation.new(time_rotate)
12
+ end
13
+
14
+ def rotate?(file)
15
+ @size_strategy.rotate?(file) || @time_strategy.rotate?(file)
16
+ end
17
+
18
+ def needs_periodic_check?
19
+ true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module LogStash
3
+ module Outputs
4
+ class OSS
5
+ class SizeBasedRotation
6
+ attr_reader :size_rotate
7
+
8
+ def initialize(size_rotate)
9
+ if size_rotate <= 0
10
+ raise LogStash::ConfigurationError, "Logstash OSS output has wrong configuration: size_rotate must be positive if strategy is `size`"
11
+ end
12
+ @size_rotate = size_rotate
13
+ end
14
+
15
+ def rotate?(file)
16
+ file.size >= @size_rotate
17
+ end
18
+
19
+ def needs_periodic_check?
20
+ false
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module LogStash
3
+ module Outputs
4
+ class OSS
5
+ class TimeBasedRotation
6
+ attr_reader :time_rotate
7
+
8
+ def initialize(time_rotate)
9
+ if time_rotate <= 0
10
+ raise LogStash::ConfigurationError, "Logstash OSS output has wrong configuration: time_rotate must be positive if strategy is `time`"
11
+ end
12
+ @time_rotate = time_rotate * 60
13
+ end
14
+
15
+ def rotate?(file)
16
+ file.size > 0 && (Time.now - file.ctime) >= @time_rotate
17
+ end
18
+
19
+ def needs_periodic_check?
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ module LogStash
4
+ module Outputs
5
+ class OSS
6
+ class TemporaryFile
7
+ extend Forwardable
8
+
9
+ def_delegators :@file, :path, :write, :close, :fsync
10
+
11
+ attr_reader :file
12
+
13
+ def initialize(file, object_key, temporary_path)
14
+ @file = file
15
+ @object_key = object_key
16
+ @temporary_path = temporary_path
17
+ @creation_time = Time.now
18
+ end
19
+
20
+ def ctime
21
+ @creation_time
22
+ end
23
+
24
+ def temporary_path
25
+ @temporary_path
26
+ end
27
+
28
+ def size
29
+ begin
30
+ @file.size
31
+ rescue IOError
32
+ ::File.size(path)
33
+ end
34
+ end
35
+
36
+ def key
37
+ @object_key.gsub(/^\//, "")
38
+ end
39
+
40
+ def delete!
41
+ @file.close rescue IOError
42
+ FileUtils.rm_r(@temporary_path, :secure => true)
43
+ end
44
+
45
+ def empty?
46
+ size == 0
47
+ end
48
+
49
+ def self.create_existing_file(path, temporary_directory)
50
+ # path is #{temporary_directory}/${uuid}/${prefix}/${key}
51
+ elements = Pathname.new(path).relative_path_from(Pathname.new(temporary_directory)).to_s.split(::File::SEPARATOR)
52
+ uuid = elements[0]
53
+ object_key = ::File.join(elements.slice(1, elements.size - 1))
54
+ TemporaryFile.new(::File.open(path, "r"), object_key, ::File.join(temporary_directory, uuid))
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ module LogStash
4
+ module Outputs
5
+ class OSS
6
+ class Version
7
+ VERSION = "0.1.1"
8
+ def self.version
9
+ VERSION
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
Binary file
@@ -0,0 +1,30 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-output-oss'
3
+ s.version = '0.1.1'
4
+ s.licenses = ['Apache-2.0']
5
+ s.summary = 'Sends Logstash events to the Aliyun Object Storage Service'
6
+ s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gem-name. This gem is not a stand-alone program"
7
+ s.authors = ['jinhu.wu']
8
+ s.email = 'jinhu.wu.nju@gmail.com'
9
+ s.require_paths = ['lib']
10
+ s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
11
+
12
+ # Files
13
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
19
+
20
+ # Gem dependencies
21
+ s.add_runtime_dependency "logstash-core-plugin-api", "~> 2.0"
22
+ s.add_runtime_dependency "logstash-codec-plain", "~> 3.0"
23
+ s.add_runtime_dependency "concurrent-ruby", "~> 1.0"
24
+ s.add_runtime_dependency "uuid", '~> 2.3', '>= 2.3.9'
25
+ s.add_development_dependency "logstash-devutils", "~> 1.3"
26
+ s.add_development_dependency "logstash-codec-line", "~> 3.0"
27
+ s.platform = 'java'
28
+ s.add_runtime_dependency 'jar-dependencies', '~> 0.3'
29
+ s.requirements << 'jar com.aliyun.oss:aliyun-sdk-oss, 3.4.0'
30
+ end