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