logstash-output-google_cloud_storage 4.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +77 -0
  3. data/CONTRIBUTORS +18 -0
  4. data/Gemfile +11 -0
  5. data/LICENSE +13 -0
  6. data/NOTICE.TXT +5 -0
  7. data/README.md +100 -0
  8. data/docs/index.asciidoc +305 -0
  9. data/lib/logstash-output-google_cloud_storage_jars.rb +35 -0
  10. data/lib/logstash/outputs/gcs/client.rb +88 -0
  11. data/lib/logstash/outputs/gcs/log_rotate.rb +77 -0
  12. data/lib/logstash/outputs/gcs/path_factory.rb +119 -0
  13. data/lib/logstash/outputs/gcs/temp_log_file.rb +111 -0
  14. data/lib/logstash/outputs/gcs/worker_pool.rb +47 -0
  15. data/lib/logstash/outputs/google_cloud_storage.rb +276 -0
  16. data/logstash-output-google_cloud_storage.gemspec +33 -0
  17. data/spec/fixtures/credentials.json +8 -0
  18. data/spec/outputs/gcs/client_spec.rb +18 -0
  19. data/spec/outputs/gcs/log_rotate_spec.rb +129 -0
  20. data/spec/outputs/gcs/path_factory_spec.rb +189 -0
  21. data/spec/outputs/gcs/temp_log_file_spec.rb +155 -0
  22. data/spec/outputs/gcs/worker_pool_spec.rb +29 -0
  23. data/spec/outputs/google_cloud_storage_spec.rb +23 -0
  24. data/spec/spec_helper.rb +3 -0
  25. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-core/2.9.6/jackson-core-2.9.6.jar +0 -0
  26. data/vendor/jar-dependencies/com/google/api-client/google-api-client/1.23.0/google-api-client-1.23.0.jar +0 -0
  27. data/vendor/jar-dependencies/com/google/api-client/google-api-client/1.24.1/google-api-client-1.24.1.jar +0 -0
  28. data/vendor/jar-dependencies/com/google/api-client/google-api-client/1.27.0/google-api-client-1.27.0.jar +0 -0
  29. data/vendor/jar-dependencies/com/google/api/api-common/1.5.0/api-common-1.5.0.jar +0 -0
  30. data/vendor/jar-dependencies/com/google/api/api-common/1.7.0/api-common-1.7.0.jar +0 -0
  31. data/vendor/jar-dependencies/com/google/api/gax-httpjson/0.40.0/gax-httpjson-0.40.0.jar +0 -0
  32. data/vendor/jar-dependencies/com/google/api/gax-httpjson/0.47.0/gax-httpjson-0.47.0.jar +0 -0
  33. data/vendor/jar-dependencies/com/google/api/gax-httpjson/0.59.0/gax-httpjson-0.59.0.jar +0 -0
  34. data/vendor/jar-dependencies/com/google/api/gax/1.23.0/gax-1.23.0.jar +0 -0
  35. data/vendor/jar-dependencies/com/google/api/gax/1.30.0/gax-1.30.0.jar +0 -0
  36. data/vendor/jar-dependencies/com/google/api/gax/1.42.0/gax-1.42.0.jar +0 -0
  37. data/vendor/jar-dependencies/com/google/api/grpc/proto-google-common-protos/1.12.0/proto-google-common-protos-1.12.0.jar +0 -0
  38. data/vendor/jar-dependencies/com/google/api/grpc/proto-google-common-protos/1.14.0/proto-google-common-protos-1.14.0.jar +0 -0
  39. data/vendor/jar-dependencies/com/google/api/grpc/proto-google-common-protos/1.7.0/proto-google-common-protos-1.7.0.jar +0 -0
  40. data/vendor/jar-dependencies/com/google/api/grpc/proto-google-iam-v1/0.12.0/proto-google-iam-v1-0.12.0.jar +0 -0
  41. data/vendor/jar-dependencies/com/google/api/grpc/proto-google-iam-v1/0.8.0/proto-google-iam-v1-0.8.0.jar +0 -0
  42. data/vendor/jar-dependencies/com/google/apis/google-api-services-storage/v1-rev114-1.23.0/google-api-services-storage-v1-rev114-1.23.0.jar +0 -0
  43. data/vendor/jar-dependencies/com/google/apis/google-api-services-storage/v1-rev135-1.24.1/google-api-services-storage-v1-rev135-1.24.1.jar +0 -0
  44. data/vendor/jar-dependencies/com/google/apis/google-api-services-storage/v1-rev20181109-1.27.0/google-api-services-storage-v1-rev20181109-1.27.0.jar +0 -0
  45. data/vendor/jar-dependencies/com/google/auth/google-auth-library-credentials/0.10.0/google-auth-library-credentials-0.10.0.jar +0 -0
  46. data/vendor/jar-dependencies/com/google/auth/google-auth-library-credentials/0.13.0/google-auth-library-credentials-0.13.0.jar +0 -0
  47. data/vendor/jar-dependencies/com/google/auth/google-auth-library-credentials/0.9.0/google-auth-library-credentials-0.9.0.jar +0 -0
  48. data/vendor/jar-dependencies/com/google/auth/google-auth-library-oauth2-http/0.10.0/google-auth-library-oauth2-http-0.10.0.jar +0 -0
  49. data/vendor/jar-dependencies/com/google/auth/google-auth-library-oauth2-http/0.13.0/google-auth-library-oauth2-http-0.13.0.jar +0 -0
  50. data/vendor/jar-dependencies/com/google/auth/google-auth-library-oauth2-http/0.9.0/google-auth-library-oauth2-http-0.9.0.jar +0 -0
  51. data/vendor/jar-dependencies/com/google/cloud/google-cloud-core-http/1.25.0/google-cloud-core-http-1.25.0.jar +0 -0
  52. data/vendor/jar-dependencies/com/google/cloud/google-cloud-core-http/1.39.0/google-cloud-core-http-1.39.0.jar +0 -0
  53. data/vendor/jar-dependencies/com/google/cloud/google-cloud-core-http/1.65.0/google-cloud-core-http-1.65.0.jar +0 -0
  54. data/vendor/jar-dependencies/com/google/cloud/google-cloud-core/1.25.0/google-cloud-core-1.25.0.jar +0 -0
  55. data/vendor/jar-dependencies/com/google/cloud/google-cloud-core/1.39.0/google-cloud-core-1.39.0.jar +0 -0
  56. data/vendor/jar-dependencies/com/google/cloud/google-cloud-core/1.65.0/google-cloud-core-1.65.0.jar +0 -0
  57. data/vendor/jar-dependencies/com/google/cloud/google-cloud-storage/1.25.0/google-cloud-storage-1.25.0.jar +0 -0
  58. data/vendor/jar-dependencies/com/google/cloud/google-cloud-storage/1.39.0/google-cloud-storage-1.39.0.jar +0 -0
  59. data/vendor/jar-dependencies/com/google/cloud/google-cloud-storage/1.65.0/google-cloud-storage-1.65.0.jar +0 -0
  60. data/vendor/jar-dependencies/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar +0 -0
  61. data/vendor/jar-dependencies/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar +0 -0
  62. data/vendor/jar-dependencies/com/google/code/gson/gson/2.7/gson-2.7.jar +0 -0
  63. data/vendor/jar-dependencies/com/google/errorprone/error_prone_annotations/2.1.3/error_prone_annotations-2.1.3.jar +0 -0
  64. data/vendor/jar-dependencies/com/google/errorprone/error_prone_annotations/2.2.0/error_prone_annotations-2.2.0.jar +0 -0
  65. data/vendor/jar-dependencies/com/google/guava/guava-jdk5/17.0/guava-jdk5-17.0.jar +0 -0
  66. data/vendor/jar-dependencies/com/google/guava/guava/20.0/guava-20.0.jar +0 -0
  67. data/vendor/jar-dependencies/com/google/guava/guava/26.0-android/guava-26.0-android.jar +0 -0
  68. data/vendor/jar-dependencies/com/google/http-client/google-http-client-apache/2.0.0/google-http-client-apache-2.0.0.jar +0 -0
  69. data/vendor/jar-dependencies/com/google/http-client/google-http-client-appengine/1.23.0/google-http-client-appengine-1.23.0.jar +0 -0
  70. data/vendor/jar-dependencies/com/google/http-client/google-http-client-appengine/1.24.1/google-http-client-appengine-1.24.1.jar +0 -0
  71. data/vendor/jar-dependencies/com/google/http-client/google-http-client-appengine/1.28.0/google-http-client-appengine-1.28.0.jar +0 -0
  72. data/vendor/jar-dependencies/com/google/http-client/google-http-client-jackson/1.23.0/google-http-client-jackson-1.23.0.jar +0 -0
  73. data/vendor/jar-dependencies/com/google/http-client/google-http-client-jackson/1.24.1/google-http-client-jackson-1.24.1.jar +0 -0
  74. data/vendor/jar-dependencies/com/google/http-client/google-http-client-jackson2/1.23.0/google-http-client-jackson2-1.23.0.jar +0 -0
  75. data/vendor/jar-dependencies/com/google/http-client/google-http-client-jackson2/1.24.1/google-http-client-jackson2-1.24.1.jar +0 -0
  76. data/vendor/jar-dependencies/com/google/http-client/google-http-client-jackson2/1.28.0/google-http-client-jackson2-1.28.0.jar +0 -0
  77. data/vendor/jar-dependencies/com/google/http-client/google-http-client/1.23.0/google-http-client-1.23.0.jar +0 -0
  78. data/vendor/jar-dependencies/com/google/http-client/google-http-client/1.24.1/google-http-client-1.24.1.jar +0 -0
  79. data/vendor/jar-dependencies/com/google/http-client/google-http-client/1.28.0/google-http-client-1.28.0.jar +0 -0
  80. data/vendor/jar-dependencies/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar +0 -0
  81. data/vendor/jar-dependencies/com/google/oauth-client/google-oauth-client/1.23.0/google-oauth-client-1.23.0.jar +0 -0
  82. data/vendor/jar-dependencies/com/google/oauth-client/google-oauth-client/1.24.1/google-oauth-client-1.24.1.jar +0 -0
  83. data/vendor/jar-dependencies/com/google/oauth-client/google-oauth-client/1.27.0/google-oauth-client-1.27.0.jar +0 -0
  84. data/vendor/jar-dependencies/com/google/protobuf/protobuf-java-util/3.5.1/protobuf-java-util-3.5.1.jar +0 -0
  85. data/vendor/jar-dependencies/com/google/protobuf/protobuf-java-util/3.6.0/protobuf-java-util-3.6.0.jar +0 -0
  86. data/vendor/jar-dependencies/com/google/protobuf/protobuf-java/3.5.1/protobuf-java-3.5.1.jar +0 -0
  87. data/vendor/jar-dependencies/com/google/protobuf/protobuf-java/3.6.0/protobuf-java-3.6.0.jar +0 -0
  88. data/vendor/jar-dependencies/commons-codec/commons-codec/1.3/commons-codec-1.3.jar +0 -0
  89. data/vendor/jar-dependencies/commons-codec/commons-codec/1.9/commons-codec-1.9.jar +0 -0
  90. data/vendor/jar-dependencies/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar +0 -0
  91. data/vendor/jar-dependencies/commons-logging/commons-logging/1.2/commons-logging-1.2.jar +0 -0
  92. data/vendor/jar-dependencies/io/grpc/grpc-context/1.12.0/grpc-context-1.12.0.jar +0 -0
  93. data/vendor/jar-dependencies/io/grpc/grpc-context/1.9.0/grpc-context-1.9.0.jar +0 -0
  94. data/vendor/jar-dependencies/io/opencensus/opencensus-api/0.11.1/opencensus-api-0.11.1.jar +0 -0
  95. data/vendor/jar-dependencies/io/opencensus/opencensus-api/0.15.0/opencensus-api-0.15.0.jar +0 -0
  96. data/vendor/jar-dependencies/io/opencensus/opencensus-contrib-http-util/0.11.1/opencensus-contrib-http-util-0.11.1.jar +0 -0
  97. data/vendor/jar-dependencies/io/opencensus/opencensus-contrib-http-util/0.15.0/opencensus-contrib-http-util-0.15.0.jar +0 -0
  98. data/vendor/jar-dependencies/joda-time/joda-time/2.9.2/joda-time-2.9.2.jar +0 -0
  99. data/vendor/jar-dependencies/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar +0 -0
  100. data/vendor/jar-dependencies/org/apache/httpcomponents/httpclient/4.5.3/httpclient-4.5.3.jar +0 -0
  101. data/vendor/jar-dependencies/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar +0 -0
  102. data/vendor/jar-dependencies/org/apache/httpcomponents/httpcore/4.4.6/httpcore-4.4.6.jar +0 -0
  103. data/vendor/jar-dependencies/org/codehaus/jackson/jackson-core-asl/1.9.11/jackson-core-asl-1.9.11.jar +0 -0
  104. data/vendor/jar-dependencies/org/threeten/threetenbp/1.3.3/threetenbp-1.3.3.jar +0 -0
  105. metadata +249 -0
@@ -0,0 +1,35 @@
1
+ # AUTOGENERATED BY THE GRADLE SCRIPT. DO NOT EDIT.
2
+
3
+ require 'jar_dependencies'
4
+ require_jar('com.google.cloud', 'google-cloud-storage', '1.39.0')
5
+ require_jar('com.google.api', 'api-common', '1.7.0')
6
+ require_jar('com.google.api-client', 'google-api-client', '1.24.1')
7
+ require_jar('com.google.api', 'gax', '1.30.0')
8
+ require_jar('com.google.api', 'gax-httpjson', '0.47.0')
9
+ require_jar('com.google.api.grpc', 'proto-google-common-protos', '1.12.0')
10
+ require_jar('com.google.api.grpc', 'proto-google-iam-v1', '0.12.0')
11
+ require_jar('com.google.apis', 'google-api-services-storage', 'v1-rev135-1.24.1')
12
+ require_jar('com.google.auth', 'google-auth-library-credentials', '0.10.0')
13
+ require_jar('com.google.auth', 'google-auth-library-oauth2-http', '0.10.0')
14
+ require_jar('com.google.cloud', 'google-cloud-core', '1.39.0')
15
+ require_jar('com.google.cloud', 'google-cloud-core-http', '1.39.0')
16
+ require_jar('com.google.code.findbugs', 'jsr305', '3.0.2')
17
+ require_jar('com.google.code.gson', 'gson', '2.7')
18
+ require_jar('com.google.guava', 'guava', '20.0')
19
+ require_jar('com.google.http-client', 'google-http-client', '1.24.1')
20
+ require_jar('com.google.http-client', 'google-http-client-appengine', '1.24.1')
21
+ require_jar('com.google.http-client', 'google-http-client-jackson', '1.24.1')
22
+ require_jar('com.google.http-client', 'google-http-client-jackson2', '1.24.1')
23
+ require_jar('com.google.oauth-client', 'google-oauth-client', '1.24.1')
24
+ require_jar('com.google.protobuf', 'protobuf-java', '3.6.0')
25
+ require_jar('com.google.protobuf', 'protobuf-java-util', '3.6.0')
26
+ require_jar('commons-codec', 'commons-codec', '1.9')
27
+ require_jar('commons-logging', 'commons-logging', '1.2')
28
+ require_jar('io.grpc', 'grpc-context', '1.12.0')
29
+ require_jar('io.opencensus', 'opencensus-api', '0.15.0')
30
+ require_jar('io.opencensus', 'opencensus-contrib-http-util', '0.15.0')
31
+ require_jar('joda-time', 'joda-time', '2.9.2')
32
+ require_jar('org.apache.httpcomponents', 'httpclient', '4.5.3')
33
+ require_jar('org.apache.httpcomponents', 'httpcore', '4.4.6')
34
+ require_jar('org.codehaus.jackson', 'jackson-core-asl', '1.9.11')
35
+ require_jar('org.threeten', 'threetenbp', '1.3.3')
@@ -0,0 +1,88 @@
1
+ require 'thread'
2
+ require 'java'
3
+ require 'logstash-output-google_cloud_storage_jars.rb'
4
+
5
+ java_import 'com.google.api.gax.rpc.FixedHeaderProvider'
6
+ java_import 'com.google.api.gax.retrying.RetrySettings'
7
+ java_import 'com.google.auth.oauth2.GoogleCredentials'
8
+ java_import 'com.google.cloud.storage.BlobInfo'
9
+ java_import 'com.google.cloud.storage.StorageOptions'
10
+ java_import 'java.io.FileInputStream'
11
+ java_import 'org.threeten.bp.Duration'
12
+
13
+ module LogStash
14
+ module Outputs
15
+ module Gcs
16
+ class Client
17
+ def initialize(bucket, json_key_path, logger)
18
+ @logger = logger
19
+ @bucket = bucket
20
+
21
+ # create client
22
+ @storage = initialize_storage(json_key_path)
23
+ end
24
+
25
+ def upload_object(file_path, content_encoding, content_type)
26
+ input = FileInputStream.new(file_path)
27
+
28
+ blob_name = ::File.basename(file_path)
29
+ blob_info = com.google.cloud.storage.BlobInfo.newBuilder(@bucket, blob_name)
30
+ .setContentEncoding(content_encoding)
31
+ .setContentType(content_type)
32
+ .build
33
+
34
+ @logger.info("Uploading file to #{@bucket}/#{blob_name}")
35
+ @storage.create(blob_info, input)
36
+
37
+ input.close
38
+ @logger.info("Uploaded file to #{@bucket}/#{blob_name}")
39
+ end
40
+
41
+ def initialize_storage(json_key_path)
42
+ @logger.info("Initializing Google API client, key: #{json_key_path}")
43
+
44
+ StorageOptions.newBuilder
45
+ .setCredentials(credentials(json_key_path))
46
+ .setHeaderProvider(http_headers)
47
+ .setRetrySettings(retry_settings)
48
+ .build
49
+ .getService
50
+ end
51
+
52
+ private
53
+
54
+ def credentials(json_key_path)
55
+ return GoogleCredentials.getApplicationDefault() if nil_or_empty?(json_key_path)
56
+
57
+ key_file = FileInputStream.new(json_key_path)
58
+ GoogleCredentials.fromStream(key_file)
59
+ end
60
+
61
+ def http_headers
62
+ FixedHeaderProvider.create({ 'User-Agent' => 'Elastic/logstash-output-google_cloud_storage' })
63
+ end
64
+
65
+ def retry_settings
66
+ # backoff values taken from com.google.api.client.util.ExponentialBackOff
67
+ RetrySettings.newBuilder()
68
+ .setInitialRetryDelay(Duration.ofMillis(500))
69
+ .setRetryDelayMultiplier(1.5)
70
+ .setMaxRetryDelay(Duration.ofSeconds(60))
71
+ .setInitialRpcTimeout(Duration.ofSeconds(20))
72
+ .setRpcTimeoutMultiplier(1.5)
73
+ .setMaxRpcTimeout(Duration.ofSeconds(20))
74
+ .setTotalTimeout(Duration.ofMinutes(15))
75
+ .build
76
+ end
77
+
78
+ def api_debug(message, dataset, table)
79
+ @logger.debug(message, dataset: dataset, table: table)
80
+ end
81
+
82
+ def nil_or_empty?(param)
83
+ param.nil? || param.empty?
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: utf-8
2
+ require 'logstash/outputs/gcs/temp_log_file'
3
+ require 'concurrent'
4
+
5
+ module LogStash
6
+ module Outputs
7
+ module Gcs
8
+ class LogRotate
9
+ def initialize(path_factory, max_file_size_bytes, gzip, flush_interval_secs, gzip_encoded=false)
10
+ @path_factory = path_factory
11
+ @max_file_size_bytes = max_file_size_bytes
12
+ @gzip = gzip
13
+ @flush_interval_secs = flush_interval_secs
14
+ @gzip_encoded = gzip_encoded
15
+
16
+ @lock = Concurrent::ReentrantReadWriteLock.new
17
+ @rotate_callback = nil
18
+
19
+ rotate_log!
20
+ end
21
+
22
+ # writeln writes a message and carriage-return character to the open
23
+ # log file, rotating and syncing it if necessary.
24
+ #
25
+ # nil messages do not get written, but may cause the log to rotate
26
+ def writeln(message=nil)
27
+ @lock.with_write_lock do
28
+ rotate_log! if should_rotate?
29
+
30
+ @temp_file.write(message, "\n") unless message.nil?
31
+
32
+ @temp_file.fsync if @temp_file.time_since_sync >= @flush_interval_secs
33
+ end
34
+ end
35
+
36
+ # rotate_log! closes the current log (if it exists), notifies the
37
+ # handler, rolls the path over and opens a new log.
38
+ #
39
+ # Invariant: the old log will ALWAYS be closed and a new one will
40
+ # ALWAYS be open at the completion of this function.
41
+ def rotate_log!
42
+ @lock.with_write_lock do
43
+ unless @temp_file.nil?
44
+ @temp_file.close!
45
+ @rotate_callback.call(@temp_file.path) unless @rotate_callback.nil?
46
+ end
47
+
48
+ @path_factory.rotate_path!
49
+
50
+ path = @path_factory.current_path
51
+ @temp_file = LogStash::Outputs::Gcs::LogFileFactory.create(path, @gzip, true, @gzip_encoded)
52
+ end
53
+ end
54
+
55
+ # on_rotate sets a handler to be called when the log gets rotated.
56
+ # The handler receives the path to the rotated out log as a string.
57
+ def on_rotate(&block)
58
+ @lock.with_write_lock do
59
+ @rotate_callback = block
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def should_rotate?
66
+ @lock.with_read_lock do
67
+ path_changed = @path_factory.should_rotate?
68
+ rotate_on_size = @max_file_size_bytes > 0
69
+ too_big = @temp_file.size >= @max_file_size_bytes
70
+
71
+ path_changed || (rotate_on_size && too_big)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,119 @@
1
+ # encoding: utf-8
2
+ require 'thread'
3
+
4
+ module LogStash
5
+ module Outputs
6
+ module Gcs
7
+ # PathFactory creates paths for rotating files.
8
+ class PathFactory
9
+ def initialize(directory, prefix, include_host, date_pattern, include_part, include_uuid, is_gzipped)
10
+ @path_lock = Mutex.new
11
+
12
+ pattern = '%{prefix}'
13
+ pattern += '_%{host}' if include_host
14
+ pattern += '_%{date}'
15
+ @base_pattern = pattern
16
+
17
+ pattern += '.part%{partf}' if include_part
18
+ pattern += '.%{uuid}' if include_uuid
19
+ pattern += '.log'
20
+ pattern += '.gz' if is_gzipped
21
+ @pattern = pattern
22
+
23
+ @prefix = prefix
24
+ @directory = directory
25
+ @date_pattern = date_pattern
26
+
27
+ @part_number = starting_part
28
+ @current = template_variables
29
+ end
30
+
31
+ # Rotates the path to the next one in sequence. If the path has a part number
32
+ # and the base path (date/hostname) haven't changed the part number is incremented.
33
+ # Returns the path that was rotated out
34
+ def rotate_path!
35
+ last_path = current_path
36
+
37
+ @path_lock.synchronize {
38
+ @part_number = (next_base == current_base) ? @part_number + 1 : 0
39
+ @current = template_variables
40
+ }
41
+
42
+ last_path
43
+ end
44
+
45
+ # Checks if the file is ready to rotate because the timestamp changed.
46
+ def should_rotate?
47
+ @path_lock.synchronize {
48
+ next_base != current_base
49
+ }
50
+ end
51
+
52
+ # Returns the full path to the current file including parent directory.
53
+ def current_path(vars=nil)
54
+ @path_lock.synchronize {
55
+ filename = @pattern % (vars || @current)
56
+ ::File.join(@directory, filename)
57
+ }
58
+ end
59
+
60
+ private
61
+
62
+ # search through the directory for a file with the same base, and if it exists,
63
+ # set our part to be the max + 1 so we don't clobber existing files.
64
+ def starting_part
65
+ return 0 unless ::File.directory? @directory
66
+
67
+ base_path = ::File.join(@directory, next_base)
68
+
69
+ part_numbers = Dir.glob(base_path + '.part*').map do |item|
70
+ match = /^.*\.part(?<part_num>\d+).*$/.match(item)
71
+ next if match.nil?
72
+ match[:part_num].to_i
73
+ end
74
+
75
+ part_numbers.any? ? part_numbers.max + 1 : 0
76
+ end
77
+
78
+ def template_variables
79
+ {
80
+ prefix: @prefix,
81
+ host: Socket.gethostname,
82
+ date: Time.now.strftime(@date_pattern),
83
+ partf: '%03d' % @part_number,
84
+ uuid: SecureRandom.uuid
85
+ }
86
+ end
87
+
88
+ def next_base
89
+ @base_pattern % template_variables
90
+ end
91
+
92
+ def current_base
93
+ @base_pattern % @current
94
+ end
95
+ end
96
+
97
+ # PathFactoryBuilder makes the long PathFactory constructor chain more readable.
98
+ class PathFactoryBuilder
99
+ def self.build
100
+ builder = new
101
+ yield builder
102
+ builder.build_path_factory
103
+ end
104
+
105
+ def self.builder_setter(*names)
106
+ names.each do |name|
107
+ define_method("set_#{name}") {|arg| instance_variable_set("@#{name}", arg)}
108
+ end
109
+ end
110
+
111
+ builder_setter :directory, :prefix, :include_host, :date_pattern, :include_part, :include_uuid, :is_gzipped
112
+
113
+ def build_path_factory
114
+ PathFactory.new(@directory, @prefix, @include_host, @date_pattern, @include_part, @include_uuid, @is_gzipped)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,111 @@
1
+ # encoding: utf-8
2
+ require 'zlib'
3
+ require 'concurrent'
4
+ require 'time'
5
+
6
+ module LogStash
7
+ module Outputs
8
+ module Gcs
9
+ # LogFileFactory creates a LogFile according to user specification
10
+ # optionally gzipping it and creating mutexes around modification
11
+ # points.
12
+ class LogFileFactory
13
+ def self.create(path, gzip, synchronize=true, gzip_encoded=false)
14
+ lf = LogStash::Outputs::Gcs::PlainLogFile.new(path)
15
+ lf = LogStash::Outputs::Gcs::GzipLogFile.new(lf) if gzip
16
+ lf = LogStash::Outputs::Gcs::GzipLogFile.new(lf) if gzip_encoded
17
+ lf = LogStash::Outputs::Gcs::SynchronizedLogFile.new(lf) if synchronize
18
+
19
+ lf
20
+ end
21
+ end
22
+
23
+ # PlainLogFile writes events to a plain text file.
24
+ class PlainLogFile
25
+ attr_reader :path, :fd
26
+
27
+ def initialize(path)
28
+ @path = path
29
+ @fd = ::File.new(path, 'a+')
30
+ @last_sync = Time.now
31
+ end
32
+
33
+ def write(*contents)
34
+ contents.each { |c| @fd.write(c) }
35
+ end
36
+
37
+ def fsync
38
+ @fd.fsync
39
+ @last_sync = Time.now
40
+ end
41
+
42
+ def close!
43
+ @fd.fsync
44
+ @fd.close
45
+ end
46
+
47
+ def size
48
+ ::File.stat(@path).size
49
+ end
50
+
51
+ def time_since_sync
52
+ Time.now - @last_sync
53
+ end
54
+ end
55
+
56
+ # GzipLogFile wraps another log file and writes events through it.
57
+ class GzipLogFile
58
+ attr_reader :fd
59
+
60
+ def initialize(child)
61
+ @child = child
62
+ @fd = Zlib::GzipWriter.new(child.fd)
63
+ end
64
+
65
+ def write(*contents)
66
+ contents.each { |c| @fd.write(c) }
67
+ end
68
+
69
+ def fsync
70
+ @fd.flush
71
+ @child.fsync
72
+ end
73
+
74
+ def close!
75
+ fsync
76
+ # The Gzip writer closes the underlying IO after
77
+ # appending the Gzip footer.
78
+ @fd.close
79
+ end
80
+
81
+ def method_missing(method_name, *args, &block)
82
+ @child.send(method_name, *args, &block)
83
+ end
84
+ end
85
+
86
+ # SynchronizedLogFile wraps another log file and uses reentrant locks
87
+ # around its methods to prevent concurrent modification.
88
+ class SynchronizedLogFile
89
+ def initialize(child)
90
+ @child = child
91
+ @lock = Concurrent::ReentrantReadWriteLock.new
92
+ end
93
+
94
+ def time_since_sync
95
+ @lock.with_read_lock { @child.time_since_sync }
96
+ end
97
+
98
+ def path
99
+ @lock.with_read_lock { @child.path }
100
+ end
101
+
102
+ def method_missing(method_name, *args, &block)
103
+ # unless otherwise specified, get a write lock
104
+ @lock.with_write_lock do
105
+ @child.send(method_name, *args, &block)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ require 'thread'
3
+ require 'concurrent'
4
+
5
+ module LogStash
6
+ module Outputs
7
+ module Gcs
8
+ # WorkerPool creates a pool of workers that can handle jobs.
9
+ class WorkerPool
10
+ attr_reader :workers
11
+
12
+ def initialize(max_threads, synchronous=false)
13
+ @synchronous = synchronous
14
+
15
+ # set queue depth to the be the same as the number of threads so
16
+ # there's at most one pending job each when the plugin quits
17
+ @workers = Concurrent::ThreadPoolExecutor.new(
18
+ min_threads: 1,
19
+ max_threads: max_threads,
20
+ max_queue: max_threads,
21
+ fallback_policy: :caller_runs
22
+ )
23
+ end
24
+
25
+ # Submits a job to the worker pool, raises an error if the pool has
26
+ # already been stopped.
27
+ def post(&block)
28
+ raise 'Pool already stopped' unless @workers.running?
29
+
30
+ if @synchronous
31
+ block.call
32
+ else
33
+ @workers.post do
34
+ block.call
35
+ end
36
+ end
37
+ end
38
+
39
+ # Stops the worker pool
40
+ def stop!
41
+ @workers.shutdown
42
+ @workers.wait_for_termination
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end