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,276 @@
|
|
|
1
|
+
# [source,txt]
|
|
2
|
+
# -----
|
|
3
|
+
# encoding: utf-8
|
|
4
|
+
# Author: Rodrigo De Castro <rdc@google.com>
|
|
5
|
+
# Date: 2013-09-20
|
|
6
|
+
#
|
|
7
|
+
# Copyright 2013 Google Inc.
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
# -----
|
|
21
|
+
require 'logstash/outputs/gcs/client'
|
|
22
|
+
require "logstash/outputs/base"
|
|
23
|
+
require "logstash/outputs/gcs/path_factory"
|
|
24
|
+
require "logstash/outputs/gcs/worker_pool"
|
|
25
|
+
require "logstash/outputs/gcs/log_rotate"
|
|
26
|
+
require "logstash/namespace"
|
|
27
|
+
require "logstash/json"
|
|
28
|
+
require "stud/interval"
|
|
29
|
+
require "thread"
|
|
30
|
+
require "zlib"
|
|
31
|
+
|
|
32
|
+
# Summary: plugin to upload log events to Google Cloud Storage (GCS), rolling
|
|
33
|
+
# files based on the date pattern provided as a configuration setting. Events
|
|
34
|
+
# are written to files locally and, once file is closed, this plugin uploads
|
|
35
|
+
# it to the configured bucket.
|
|
36
|
+
#
|
|
37
|
+
# For more info on Google Cloud Storage, please go to:
|
|
38
|
+
# https://cloud.google.com/products/cloud-storage
|
|
39
|
+
#
|
|
40
|
+
# In order to use this plugin, a Google service account must be used. For
|
|
41
|
+
# more information, please refer to:
|
|
42
|
+
# https://developers.google.com/storage/docs/authentication#service_accounts
|
|
43
|
+
#
|
|
44
|
+
# Recommendation: experiment with the settings depending on how much log
|
|
45
|
+
# data you generate, so the uploader can keep up with the generated logs.
|
|
46
|
+
# Using gzip output can be a good option to reduce network traffic when
|
|
47
|
+
# uploading the log files and in terms of storage costs as well.
|
|
48
|
+
#
|
|
49
|
+
# USAGE:
|
|
50
|
+
# This is an example of logstash config:
|
|
51
|
+
#
|
|
52
|
+
# [source,json]
|
|
53
|
+
# --------------------------
|
|
54
|
+
# output {
|
|
55
|
+
# google_cloud_storage {
|
|
56
|
+
# bucket => "my_bucket" (required)
|
|
57
|
+
# json_key_file => "/path/to/privatekey.json" (optional)
|
|
58
|
+
# temp_directory => "/tmp/logstash-gcs" (optional)
|
|
59
|
+
# log_file_prefix => "logstash_gcs" (optional)
|
|
60
|
+
# max_file_size_kbytes => 1024 (optional)
|
|
61
|
+
# output_format => "plain" (optional)
|
|
62
|
+
# date_pattern => "%Y-%m-%dT%H:00" (optional)
|
|
63
|
+
# flush_interval_secs => 2 (optional)
|
|
64
|
+
# gzip => false (optional)
|
|
65
|
+
# gzip_content_encoding => false (optional)
|
|
66
|
+
# uploader_interval_secs => 60 (optional)
|
|
67
|
+
# upload_synchronous => false (optional)
|
|
68
|
+
# }
|
|
69
|
+
# }
|
|
70
|
+
# --------------------------
|
|
71
|
+
#
|
|
72
|
+
# Improvements TODO list:
|
|
73
|
+
# * Support logstash event variables to determine filename.
|
|
74
|
+
# * Turn Google API code into a Plugin Mixin (like AwsConfig).
|
|
75
|
+
# * There's no recover method, so if logstash/plugin crashes, files may not
|
|
76
|
+
# be uploaded to GCS.
|
|
77
|
+
# * Allow user to configure file name.
|
|
78
|
+
class LogStash::Outputs::GoogleCloudStorage < LogStash::Outputs::Base
|
|
79
|
+
config_name "google_cloud_storage"
|
|
80
|
+
|
|
81
|
+
concurrency :single
|
|
82
|
+
|
|
83
|
+
# GCS bucket name, without "gs://" or any other prefix.
|
|
84
|
+
config :bucket, :validate => :string, :required => true
|
|
85
|
+
|
|
86
|
+
# GCS path to private key file.
|
|
87
|
+
config :key_path, :validate => :string, :obsolete => 'Use json_key_file or ADC instead.'
|
|
88
|
+
|
|
89
|
+
# GCS private key password.
|
|
90
|
+
config :key_password, :validate => :string, :deprecated => 'Use json_key_file or ADC instead.'
|
|
91
|
+
|
|
92
|
+
# GCS service account.
|
|
93
|
+
config :service_account, :validate => :string, :deprecated => 'Use json_key_file or ADC instead.'
|
|
94
|
+
|
|
95
|
+
# Directory where temporary files are stored.
|
|
96
|
+
# Defaults to /tmp/logstash-gcs-<random-suffix>
|
|
97
|
+
config :temp_directory, :validate => :string, :default => ""
|
|
98
|
+
|
|
99
|
+
# Log file prefix. Log file will follow the format:
|
|
100
|
+
# <prefix>_hostname_date<.part?>.log
|
|
101
|
+
config :log_file_prefix, :validate => :string, :default => "logstash_gcs"
|
|
102
|
+
|
|
103
|
+
# Sets max file size in kbytes. 0 disable max file check.
|
|
104
|
+
config :max_file_size_kbytes, :validate => :number, :default => 10000
|
|
105
|
+
|
|
106
|
+
# The event format you want to store in files. Defaults to plain text.
|
|
107
|
+
config :output_format, :validate => [ "json", "plain" ], :default => "plain"
|
|
108
|
+
|
|
109
|
+
# Time pattern for log file, defaults to hourly files.
|
|
110
|
+
# Must Time.strftime patterns: www.ruby-doc.org/core-2.0/Time.html#method-i-strftime
|
|
111
|
+
config :date_pattern, :validate => :string, :default => "%Y-%m-%dT%H:00"
|
|
112
|
+
|
|
113
|
+
# Flush interval in seconds for flushing writes to log files. 0 will flush
|
|
114
|
+
# on every message.
|
|
115
|
+
config :flush_interval_secs, :validate => :number, :default => 2
|
|
116
|
+
|
|
117
|
+
# Gzip output stream when writing events to log files, set
|
|
118
|
+
# `Content-Type` to `application/gzip` instead of `text/plain`, and
|
|
119
|
+
# use file suffix `.log.gz` instead of `.log`.
|
|
120
|
+
config :gzip, :validate => :boolean, :default => false
|
|
121
|
+
|
|
122
|
+
# Gzip output stream when writing events to log files and set
|
|
123
|
+
# `Content-Encoding` to `gzip`.
|
|
124
|
+
config :gzip_content_encoding, :validate => :boolean, :default => false
|
|
125
|
+
|
|
126
|
+
# Uploader interval when uploading new files to GCS. Adjust time based
|
|
127
|
+
# on your time pattern (for example, for hourly files, this interval can be
|
|
128
|
+
# around one hour).
|
|
129
|
+
config :uploader_interval_secs, :validate => :number, :default => 60
|
|
130
|
+
|
|
131
|
+
# Should the hostname be included in the file name?
|
|
132
|
+
config :include_hostname, :validate => :boolean, :default => true
|
|
133
|
+
|
|
134
|
+
# Should a UUID be included in the file name?
|
|
135
|
+
config :include_uuid, :validate => :boolean, :default => false
|
|
136
|
+
|
|
137
|
+
# The path to the service account's JSON credentials file.
|
|
138
|
+
# Application Default Credentials (ADC) are used if the path is blank.
|
|
139
|
+
# See: https://cloud.google.com/docs/authentication/production
|
|
140
|
+
#
|
|
141
|
+
# You must run on GCP for ADC to work.
|
|
142
|
+
config :json_key_file, :validate => :string, :default => ""
|
|
143
|
+
|
|
144
|
+
# When true, files are uploaded by the event processing thread as soon as a file is ready.
|
|
145
|
+
# When false, (the default behaviour), files will be uploaded in a dedicated thread.
|
|
146
|
+
#
|
|
147
|
+
# Enabling this option provides greater likelihood that all generated files will be
|
|
148
|
+
# to GCS, especially in the event of a graceful shutdown of logstash, such as when an
|
|
149
|
+
# input plugin reaches the end of events. This comes at the price of introducing delays
|
|
150
|
+
# in the event processing pipeline as files are uploaded.
|
|
151
|
+
#
|
|
152
|
+
# When this feature is enabled, the uploader_interval_secs option has no effect.
|
|
153
|
+
config :upload_synchronous, :validate => :boolean, :default => false
|
|
154
|
+
|
|
155
|
+
config :max_concurrent_uploads, :validate => :number, :default => 5
|
|
156
|
+
|
|
157
|
+
public
|
|
158
|
+
def register
|
|
159
|
+
@logger.debug('Registering Google Cloud Storage plugin')
|
|
160
|
+
|
|
161
|
+
@workers = LogStash::Outputs::Gcs::WorkerPool.new(@max_concurrent_uploads, @upload_synchronous)
|
|
162
|
+
initialize_temp_directory
|
|
163
|
+
initialize_path_factory
|
|
164
|
+
initialize_log_rotater
|
|
165
|
+
|
|
166
|
+
initialize_google_client
|
|
167
|
+
|
|
168
|
+
start_uploader
|
|
169
|
+
|
|
170
|
+
@content_type = @gzip ? 'application/gzip' : 'text/plain'
|
|
171
|
+
@content_encoding = @gzip_content_encoding ? 'gzip' : 'identity'
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Method called for each log event. It writes the event to the current output
|
|
175
|
+
# file, flushing depending on flush interval configuration.
|
|
176
|
+
public
|
|
177
|
+
def receive(event)
|
|
178
|
+
@logger.debug('Received event', :event => event)
|
|
179
|
+
|
|
180
|
+
if @output_format == 'json'
|
|
181
|
+
message = LogStash::Json.dump(event.to_hash)
|
|
182
|
+
else
|
|
183
|
+
message = event.to_s
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
@log_rotater.writeln(message)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
public
|
|
190
|
+
def close
|
|
191
|
+
@logger.debug('Stopping the plugin, uploading the remaining files.')
|
|
192
|
+
Stud.stop!(@registration_thread) unless @registration_thread.nil?
|
|
193
|
+
|
|
194
|
+
# Force rotate the log. If it contains data it will be submitted
|
|
195
|
+
# to the work pool and will be uploaded before the plugin stops.
|
|
196
|
+
@log_rotater.rotate_log!
|
|
197
|
+
@workers.stop!
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# Creates temporary directory, if it does not exist.
|
|
204
|
+
#
|
|
205
|
+
# A random suffix is appended to the temporary directory
|
|
206
|
+
def initialize_temp_directory
|
|
207
|
+
require "stud/temporary"
|
|
208
|
+
|
|
209
|
+
if @temp_directory.empty?
|
|
210
|
+
@temp_directory = Stud::Temporary.directory('logstash-gcs')
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
FileUtils.mkdir_p(@temp_directory) unless File.directory?(@temp_directory)
|
|
214
|
+
|
|
215
|
+
@logger.info("Using temporary directory: #{@temp_directory}")
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def initialize_path_factory
|
|
219
|
+
@path_factory = LogStash::Outputs::Gcs::PathFactoryBuilder.build do |builder|
|
|
220
|
+
builder.set_directory(@temp_directory)
|
|
221
|
+
builder.set_prefix(@log_file_prefix)
|
|
222
|
+
builder.set_include_host(@include_hostname)
|
|
223
|
+
builder.set_date_pattern(@date_pattern)
|
|
224
|
+
builder.set_include_part(@max_file_size_kbytes > 0)
|
|
225
|
+
builder.set_include_uuid(@include_uuid)
|
|
226
|
+
builder.set_is_gzipped(@gzip)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# start_uploader periodically sends flush events through the log rotater
|
|
231
|
+
def start_uploader
|
|
232
|
+
@registration_thread = Thread.new do
|
|
233
|
+
Stud.interval(@uploader_interval_secs) do
|
|
234
|
+
@log_rotater.writeln(nil)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
##
|
|
240
|
+
# Initializes Google Client instantiating client and authorizing access.
|
|
241
|
+
def initialize_google_client
|
|
242
|
+
@client = LogStash::Outputs::Gcs::Client.new(@bucket, @json_key_file, @logger)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
##
|
|
246
|
+
# Uploads a local file to the configured bucket.
|
|
247
|
+
def upload_object(filename)
|
|
248
|
+
@client.upload_object(filename, @content_encoding, @content_type)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def upload_and_delete(filename)
|
|
252
|
+
file_size = File.stat(filename).size
|
|
253
|
+
|
|
254
|
+
if file_size > 0
|
|
255
|
+
upload_object(filename)
|
|
256
|
+
else
|
|
257
|
+
@logger.debug('File size is zero, skip upload.', :filename => filename)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
@logger.debug('Delete local temporary file', :filename => filename)
|
|
261
|
+
File.delete(filename)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def initialize_log_rotater
|
|
265
|
+
max_file_size = @max_file_size_kbytes * 1024
|
|
266
|
+
@log_rotater = LogStash::Outputs::Gcs::LogRotate.new(@path_factory, max_file_size, @gzip, @flush_interval_secs, @gzip_content_encoding)
|
|
267
|
+
|
|
268
|
+
@log_rotater.on_rotate do |filename|
|
|
269
|
+
@logger.info("Rotated out file: #{filename}")
|
|
270
|
+
@workers.post do
|
|
271
|
+
upload_and_delete(filename)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
attr_accessor :active
|
|
276
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'logstash-output-google_cloud_storage'
|
|
3
|
+
s.version = '4.0.0'
|
|
4
|
+
s.licenses = ['Apache-2.0']
|
|
5
|
+
s.summary = "plugin to upload log events to Google Cloud Storage (GCS)"
|
|
6
|
+
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
|
|
7
|
+
s.authors = ["Elastic"]
|
|
8
|
+
s.email = 'info@elastic.co'
|
|
9
|
+
s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
|
|
10
|
+
s.require_paths = ["lib", "vendor/jar-dependencies"]
|
|
11
|
+
|
|
12
|
+
# Files
|
|
13
|
+
s.files = Dir["lib/**/*","spec/**/*","*.gemspec","*.md","CONTRIBUTORS","Gemfile","LICENSE","NOTICE.TXT", "vendor/jar-dependencies/**/*.jar", "vendor/jar-dependencies/**/*.rb", "VERSION", "docs/**/*"]
|
|
14
|
+
|
|
15
|
+
# Tests
|
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
|
17
|
+
|
|
18
|
+
# Special flag to let us know this is actually a logstash plugin
|
|
19
|
+
s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
|
|
20
|
+
|
|
21
|
+
# Gem dependencies
|
|
22
|
+
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
|
23
|
+
|
|
24
|
+
s.add_runtime_dependency 'stud'
|
|
25
|
+
s.add_runtime_dependency 'logstash-codec-plain'
|
|
26
|
+
s.add_runtime_dependency 'mime-types', '~> 2' # last version compatible with ruby 2.x
|
|
27
|
+
s.add_runtime_dependency 'concurrent-ruby' # use version bundled with Logstash to avoid platform mismatch on plugin install
|
|
28
|
+
s.add_development_dependency 'logstash-devutils'
|
|
29
|
+
|
|
30
|
+
# JARs
|
|
31
|
+
s.platform = 'java'
|
|
32
|
+
end
|
|
33
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "Dummy account from https://github.com/GoogleCloudPlatform/google-cloud-java/google-cloud-clients/google-cloud-core/src/test/java/com/google/cloud/ServiceOptionsTest.java",
|
|
3
|
+
"private_key_id": "somekeyid",
|
|
4
|
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+K2hSuFpAdrJI\nnCgcDz2M7t7bjdlsadsasad+fvRSW6TjNQZ3p5LLQY1kSZRqBqylRkzteMOyHgaR\n0Pmxh3ILCND5men43j3h4eDbrhQBuxfEMalkG92sL+PNQSETY2tnvXryOvmBRwa/\nQP/9dJfIkIDJ9Fw9N4Bhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\nknddadwkwewcVxHFhcZJO+XWf6ofLUXpRwiTZakGMn8EE1uVa2LgczOjwWHGi99MFjxSer5m9\n1tCa3/KEGKiS/YL71JvjwX3mb+cewlkcmweBKZHM2JPTk0ZednFSpVZMtycjkbLa\ndYOS8V85AgMBewECggEBAKksaldajfDZDV6nGqbFjMiizAKJolr/M3OQw16K6o3/\n0S31xIe3sSlgW0+UbYlF4U8KifhManD1apVSC3csafaspP4RZUHFhtBywLO9pR5c\nr6S5aLp+gPWFyIp1pfXbWGvc5VY/v9x7ya1VEa6rXvLsKupSeWAW4tMj3eo/64ge\nsdaceaLYw52KeBYiT6+vpsnYrEkAHO1fF/LavbLLOFJmFTMxmsNaG0tuiJHgjshB\n82DpMCbXG9YcCgI/DbzuIjsdj2JC1cascSP//3PmefWysucBQe7Jryb6NQtASmnv\nCdDw/0jmZTEjpe4S1lxfHplAhHFtdgYTvyYtaLZiVVkCgYEA8eVpof2rceecw/I6\n5ng1q3Hl2usdWV/4mZMvR0fOemacLLfocX6IYxT1zA1FFJlbXSRsJMf/Qq39mOR2\nSpW+hr4jCoHeRVYLgsbggtrevGmILAlNoqCMpGZ6vDmJpq6ECV9olliDvpPgWOP+\nmYPDreFBGxWvQrADNbRt2dmGsrsCgYEAyUHqB2wvJHFqdmeBsaacewzV8x9WgmeX\ngUIi9REwXlGDW0Mz50dxpxcKCAYn65+7TCnY5O/jmL0VRxU1J2mSWyWTo1C+17L0\n3fUqjxL1pkefwecxwecvC+gFFYdJ4CQ/MHHXU81Lwl1iWdFCd2UoGddYaOF+KNeM\nHC7cmqra+JsCgYEAlUNywzq8nUg7282E+uICfCB0LfwejuymR93CtsFgb7cRd6ak\nECR8FGfCpH8ruWJINllbQfcHVCX47ndLZwqv3oVFKh6pAS/vVI4dpOepP8++7y1u\ncoOvtreXCX6XqfrWDtKIvv0vjlHBhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\nkndj5uNl5SiuVxHFhcZJO+XWf6ofLUregtevZakGMn8EE1uVa2AY7eafmoU/nZPT\n00YB0TBATdCbn/nBSuKDESkhSg9s2GEKQZG5hBmL5uCMfo09z3SfxZIhJdlerreP\nJ7gSidI12N+EZxYd4xIJh/HFDgp7RRO87f+WJkofMQKBgGTnClK1VMaCRbJZPriw\nEfeFCoOX75MxKwXs6xgrw4W//AYGGUjDt83lD6AZP6tws7gJ2IwY/qP7+lyhjEqN\nHtfPZRGFkGZsdaksdlaksd323423d+15/UvrlRSFPNj1tWQmNKkXyRDW4IG1Oa2p\nrALStNBx5Y9t0/LQnFI4w3aG\n-----END PRIVATE KEY-----\n",
|
|
5
|
+
"client_email": "someclientid@developer.gserviceaccount.com",
|
|
6
|
+
"client_id": "someclientid.apps.googleusercontent.com",
|
|
7
|
+
"type": "service_account"
|
|
8
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'logstash/outputs/gcs/client'
|
|
4
|
+
|
|
5
|
+
describe LogStash::Outputs::Gcs::Client do
|
|
6
|
+
|
|
7
|
+
# This test is mostly to make sure the Java types, signatures and classes
|
|
8
|
+
# haven't changed being that JRuby is very relaxed.
|
|
9
|
+
describe '#initialize' do
|
|
10
|
+
let(:logger) { spy('logger') }
|
|
11
|
+
|
|
12
|
+
it 'does not throw an error when initializing' do
|
|
13
|
+
key_file = ::File.join('spec', 'fixtures', 'credentials.json')
|
|
14
|
+
key_file = ::File.absolute_path(key_file)
|
|
15
|
+
LogStash::Outputs::Gcs::Client.new('my-bucket', key_file, logger)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'logstash/outputs/gcs/log_rotate'
|
|
3
|
+
require 'logstash/outputs/gcs/path_factory'
|
|
4
|
+
require 'logstash/outputs/gcs/temp_log_file'
|
|
5
|
+
|
|
6
|
+
describe LogStash::Outputs::Gcs::LogRotate do
|
|
7
|
+
let(:tempdir){ Stud::Temporary.directory }
|
|
8
|
+
let(:path_factory) do
|
|
9
|
+
LogStash::Outputs::Gcs::PathFactoryBuilder.build do |builder|
|
|
10
|
+
builder.set_directory tempdir
|
|
11
|
+
builder.set_prefix 'prefix'
|
|
12
|
+
builder.set_include_host true
|
|
13
|
+
builder.set_date_pattern ''
|
|
14
|
+
builder.set_include_part true
|
|
15
|
+
builder.set_include_uuid true
|
|
16
|
+
builder.set_is_gzipped true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
let(:open_file_1) { double('open-temp-1', :size => 5, :path => 'one', :close! => true, :time_since_sync => 10, :fsync => true)}
|
|
20
|
+
let(:open_file_2) { double('open-temp-2', :size => 5, :path => 'two', :close! => true, :time_since_sync => 60, :fsync => true)}
|
|
21
|
+
|
|
22
|
+
describe '#initialize' do
|
|
23
|
+
it 'opens the first file' do
|
|
24
|
+
expect(LogStash::Outputs::Gcs::LogFileFactory).to receive(:create).and_return(open_file_1)
|
|
25
|
+
|
|
26
|
+
LogStash::Outputs::Gcs::LogRotate.new(path_factory, 10, false, 30)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#writeln' do
|
|
31
|
+
subject { LogStash::Outputs::Gcs::LogRotate.new(path_factory, 10, false, 30) }
|
|
32
|
+
|
|
33
|
+
it 'does not rotate if size is small and path is the same' do
|
|
34
|
+
expect(path_factory).to receive(:should_rotate?).and_return(false)
|
|
35
|
+
# once for init
|
|
36
|
+
expect(path_factory).to receive(:rotate_path!).once
|
|
37
|
+
|
|
38
|
+
subject.writeln('foo')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'rotates the file if the size is too big' do
|
|
42
|
+
# once for init, once for writeln
|
|
43
|
+
expect(path_factory).to receive(:rotate_path!).twice
|
|
44
|
+
|
|
45
|
+
subject.writeln('this line is longer than ten characters' * 1000)
|
|
46
|
+
subject.writeln('flush')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'rotates the file if the path changed' do
|
|
50
|
+
expect(path_factory).to receive(:should_rotate?).and_return(true)
|
|
51
|
+
# once for init, once for writeln
|
|
52
|
+
expect(path_factory).to receive(:rotate_path!).twice
|
|
53
|
+
|
|
54
|
+
subject.writeln('foo')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'writes the message' do
|
|
58
|
+
expect(LogStash::Outputs::Gcs::LogFileFactory).to receive(:create).and_return(open_file_1)
|
|
59
|
+
expect(open_file_1).to receive(:write).with('foo', "\n")
|
|
60
|
+
|
|
61
|
+
subject.writeln('foo')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'does not write nil messages' do
|
|
65
|
+
expect(LogStash::Outputs::Gcs::LogFileFactory).to receive(:create).and_return(open_file_1)
|
|
66
|
+
expect(open_file_1).not_to receive(:write)
|
|
67
|
+
|
|
68
|
+
subject.writeln(nil)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'does not fsync if delta less than limit' do
|
|
72
|
+
expect(LogStash::Outputs::Gcs::LogFileFactory).to receive(:create).and_return(open_file_1)
|
|
73
|
+
expect(open_file_1).not_to receive(:fsync)
|
|
74
|
+
|
|
75
|
+
subject.writeln(nil)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'fsyncs if delta greater than limit' do
|
|
79
|
+
expect(LogStash::Outputs::Gcs::LogFileFactory).to receive(:create).and_return(open_file_2)
|
|
80
|
+
expect(open_file_2).to receive(:fsync)
|
|
81
|
+
|
|
82
|
+
subject.writeln(nil)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe '#rotate_log!' do
|
|
87
|
+
subject { LogStash::Outputs::Gcs::LogRotate.new(path_factory, 10, false, 30) }
|
|
88
|
+
|
|
89
|
+
before :each do
|
|
90
|
+
allow(LogStash::Outputs::Gcs::LogFileFactory).to receive(:create).and_return(open_file_1, open_file_2)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'closes the old file' do
|
|
94
|
+
expect(open_file_1).to receive(:close!)
|
|
95
|
+
|
|
96
|
+
subject.rotate_log!
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'calls the callback with the old file name' do
|
|
100
|
+
value = nil
|
|
101
|
+
subject.on_rotate { |old_path| value = old_path }
|
|
102
|
+
|
|
103
|
+
subject.rotate_log!
|
|
104
|
+
expect(value).to eq(open_file_1.path)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'opens a new file based on the new path' do
|
|
108
|
+
expect(LogStash::Outputs::Gcs::LogFileFactory).to receive(:create).and_return(open_file_1, open_file_2)
|
|
109
|
+
expect(open_file_2).to receive(:write).with('foo', "\n")
|
|
110
|
+
|
|
111
|
+
subject.rotate_log!
|
|
112
|
+
subject.writeln('foo')
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe '#on_rotate' do
|
|
117
|
+
subject { LogStash::Outputs::Gcs::LogRotate.new(path_factory, 10, false, 30) }
|
|
118
|
+
|
|
119
|
+
it 'replaces an existing callback' do
|
|
120
|
+
value = :none
|
|
121
|
+
|
|
122
|
+
subject.on_rotate { value = :first }
|
|
123
|
+
subject.on_rotate { value = :second }
|
|
124
|
+
|
|
125
|
+
subject.rotate_log!
|
|
126
|
+
expect(value).to eq(:second)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|