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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +77 -0
- data/CONTRIBUTORS +18 -0
- data/Gemfile +11 -0
- data/LICENSE +13 -0
- data/NOTICE.TXT +5 -0
- data/README.md +100 -0
- data/docs/index.asciidoc +305 -0
- data/lib/logstash-output-google_cloud_storage_jars.rb +35 -0
- data/lib/logstash/outputs/gcs/client.rb +88 -0
- data/lib/logstash/outputs/gcs/log_rotate.rb +77 -0
- data/lib/logstash/outputs/gcs/path_factory.rb +119 -0
- data/lib/logstash/outputs/gcs/temp_log_file.rb +111 -0
- data/lib/logstash/outputs/gcs/worker_pool.rb +47 -0
- data/lib/logstash/outputs/google_cloud_storage.rb +276 -0
- data/logstash-output-google_cloud_storage.gemspec +33 -0
- data/spec/fixtures/credentials.json +8 -0
- data/spec/outputs/gcs/client_spec.rb +18 -0
- data/spec/outputs/gcs/log_rotate_spec.rb +129 -0
- data/spec/outputs/gcs/path_factory_spec.rb +189 -0
- data/spec/outputs/gcs/temp_log_file_spec.rb +155 -0
- data/spec/outputs/gcs/worker_pool_spec.rb +29 -0
- data/spec/outputs/google_cloud_storage_spec.rb +23 -0
- data/spec/spec_helper.rb +3 -0
- data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-core/2.9.6/jackson-core-2.9.6.jar +0 -0
- data/vendor/jar-dependencies/com/google/api-client/google-api-client/1.23.0/google-api-client-1.23.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api-client/google-api-client/1.24.1/google-api-client-1.24.1.jar +0 -0
- data/vendor/jar-dependencies/com/google/api-client/google-api-client/1.27.0/google-api-client-1.27.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/api-common/1.5.0/api-common-1.5.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/api-common/1.7.0/api-common-1.7.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/gax-httpjson/0.40.0/gax-httpjson-0.40.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/gax-httpjson/0.47.0/gax-httpjson-0.47.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/gax-httpjson/0.59.0/gax-httpjson-0.59.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/gax/1.23.0/gax-1.23.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/gax/1.30.0/gax-1.30.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/api/gax/1.42.0/gax-1.42.0.jar +0 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- data/vendor/jar-dependencies/com/google/auth/google-auth-library-credentials/0.10.0/google-auth-library-credentials-0.10.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/auth/google-auth-library-credentials/0.13.0/google-auth-library-credentials-0.13.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/auth/google-auth-library-credentials/0.9.0/google-auth-library-credentials-0.9.0.jar +0 -0
- 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
- 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
- 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
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-core-http/1.25.0/google-cloud-core-http-1.25.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-core-http/1.39.0/google-cloud-core-http-1.39.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-core-http/1.65.0/google-cloud-core-http-1.65.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-core/1.25.0/google-cloud-core-1.25.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-core/1.39.0/google-cloud-core-1.39.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-core/1.65.0/google-cloud-core-1.65.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-storage/1.25.0/google-cloud-storage-1.25.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-storage/1.39.0/google-cloud-storage-1.39.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/cloud/google-cloud-storage/1.65.0/google-cloud-storage-1.65.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar +0 -0
- data/vendor/jar-dependencies/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar +0 -0
- data/vendor/jar-dependencies/com/google/code/gson/gson/2.7/gson-2.7.jar +0 -0
- data/vendor/jar-dependencies/com/google/errorprone/error_prone_annotations/2.1.3/error_prone_annotations-2.1.3.jar +0 -0
- data/vendor/jar-dependencies/com/google/errorprone/error_prone_annotations/2.2.0/error_prone_annotations-2.2.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/guava/guava-jdk5/17.0/guava-jdk5-17.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/guava/guava/20.0/guava-20.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/guava/guava/26.0-android/guava-26.0-android.jar +0 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- data/vendor/jar-dependencies/com/google/http-client/google-http-client/1.23.0/google-http-client-1.23.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/http-client/google-http-client/1.24.1/google-http-client-1.24.1.jar +0 -0
- data/vendor/jar-dependencies/com/google/http-client/google-http-client/1.28.0/google-http-client-1.28.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar +0 -0
- data/vendor/jar-dependencies/com/google/oauth-client/google-oauth-client/1.23.0/google-oauth-client-1.23.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/oauth-client/google-oauth-client/1.24.1/google-oauth-client-1.24.1.jar +0 -0
- data/vendor/jar-dependencies/com/google/oauth-client/google-oauth-client/1.27.0/google-oauth-client-1.27.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/protobuf/protobuf-java-util/3.5.1/protobuf-java-util-3.5.1.jar +0 -0
- data/vendor/jar-dependencies/com/google/protobuf/protobuf-java-util/3.6.0/protobuf-java-util-3.6.0.jar +0 -0
- data/vendor/jar-dependencies/com/google/protobuf/protobuf-java/3.5.1/protobuf-java-3.5.1.jar +0 -0
- data/vendor/jar-dependencies/com/google/protobuf/protobuf-java/3.6.0/protobuf-java-3.6.0.jar +0 -0
- data/vendor/jar-dependencies/commons-codec/commons-codec/1.3/commons-codec-1.3.jar +0 -0
- data/vendor/jar-dependencies/commons-codec/commons-codec/1.9/commons-codec-1.9.jar +0 -0
- data/vendor/jar-dependencies/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar +0 -0
- data/vendor/jar-dependencies/commons-logging/commons-logging/1.2/commons-logging-1.2.jar +0 -0
- data/vendor/jar-dependencies/io/grpc/grpc-context/1.12.0/grpc-context-1.12.0.jar +0 -0
- data/vendor/jar-dependencies/io/grpc/grpc-context/1.9.0/grpc-context-1.9.0.jar +0 -0
- data/vendor/jar-dependencies/io/opencensus/opencensus-api/0.11.1/opencensus-api-0.11.1.jar +0 -0
- data/vendor/jar-dependencies/io/opencensus/opencensus-api/0.15.0/opencensus-api-0.15.0.jar +0 -0
- data/vendor/jar-dependencies/io/opencensus/opencensus-contrib-http-util/0.11.1/opencensus-contrib-http-util-0.11.1.jar +0 -0
- data/vendor/jar-dependencies/io/opencensus/opencensus-contrib-http-util/0.15.0/opencensus-contrib-http-util-0.15.0.jar +0 -0
- data/vendor/jar-dependencies/joda-time/joda-time/2.9.2/joda-time-2.9.2.jar +0 -0
- data/vendor/jar-dependencies/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar +0 -0
- data/vendor/jar-dependencies/org/apache/httpcomponents/httpclient/4.5.3/httpclient-4.5.3.jar +0 -0
- data/vendor/jar-dependencies/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar +0 -0
- data/vendor/jar-dependencies/org/apache/httpcomponents/httpcore/4.4.6/httpcore-4.4.6.jar +0 -0
- data/vendor/jar-dependencies/org/codehaus/jackson/jackson-core-asl/1.9.11/jackson-core-asl-1.9.11.jar +0 -0
- data/vendor/jar-dependencies/org/threeten/threetenbp/1.3.3/threetenbp-1.3.3.jar +0 -0
- 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
|