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.
- 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
|