logstash-output-google_cloud_storage 4.0.0-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 (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