logstash-input-azureblob-saars 0.9.9

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fda938a4d5eea4d048802825de9a58ad61ba672b
4
+ data.tar.gz: 916bf7811c7d4ae0460ad95709e4a1547e854333
5
+ SHA512:
6
+ metadata.gz: d61c1dd0d34d8e647e93a82faf23a11ca81064c66be5e10298e3ea990f89c6d208ead88fbe95fb7eff4188943577d21abdeabe2a318fdc5f6bb1342bf26fbde2
7
+ data.tar.gz: 962de5038ebd66495bad9f023f02bfd7d649fe2ae9eff70150886cc2eed8954e1b555805c7cdf3daf1b7e03f1c346e43f93004547d9dc167ac8705dd1f8cd88e
@@ -0,0 +1,7 @@
1
+ ## 2016.08.17
2
+ * Added a new configuration parameter for custom endpoint.
3
+
4
+ ## 2016.05.05
5
+ * Made the plugin to respect Logstash shutdown signal.
6
+ * Updated the *logstash-core* runtime dependency requirement to '~> 2.0'.
7
+ * Updated the *logstash-devutils* development dependency requirement to '>= 0.0.16'
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+
2
+ Copyright (c) Microsoft. All rights reserved.
3
+ Microsoft would like to thank its contributors, a list
4
+ of whom are at http://aka.ms/entlib-contributors
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License"); you
7
+ may not use this file except in compliance with the License. You may
8
+ obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15
+ implied. See the License for the specific language governing permissions
16
+ and limitations under the License.
17
+
@@ -0,0 +1,242 @@
1
+ # Logstash input plugin for Azure Storage Blobs
2
+
3
+ ## Summary
4
+ This plugin reads and parses data from Azure Storage Blobs.
5
+
6
+ ## Installation
7
+ You can install this plugin using the Logstash "plugin" or "logstash-plugin" (for newer versions of Logstash) command:
8
+ ```sh
9
+ logstash-plugin install logstash-input-azureblob
10
+ ```
11
+ For more information, see Logstash reference [Working with plugins](https://www.elastic.co/guide/en/logstash/current/working-with-plugins.html).
12
+
13
+ ## Configuration
14
+ ### Required Parameters
15
+ __*storage_account_name*__
16
+
17
+ The storage account name.
18
+
19
+ __*storage_access_key*__
20
+
21
+ The access key to the storage account.
22
+
23
+ __*container*__
24
+
25
+ The blob container name.
26
+
27
+ ### Optional Parameters
28
+ __*endpoint*__
29
+
30
+ Specifies the endpoint of Azure Service Management. The default value is `core.windows.net`.
31
+
32
+ __*registry_path*__
33
+
34
+ Specifies the file path for the registry file to record offsets and coordinate between multiple clients. The default value is `data/registry`.
35
+
36
+ Overwrite this value when there happen to be a file at the path of `data/registry` in the azure blob container.
37
+
38
+ __*interval*__
39
+
40
+ Set how many seconds to idle before checking for new logs. The default, `30`, means idle for `30` seconds.
41
+
42
+ __*registry_create_policy*__
43
+
44
+ Specifies the way to initially set offset for existing blob files.
45
+
46
+ This option only applies for registry creation.
47
+
48
+ Valid values include:
49
+
50
+ - resume
51
+ - start_over
52
+
53
+ The default, `resume`, means when the registry is initially created, it assumes all blob has been consumed and it will start to pick up any new content in the blobs.
54
+
55
+ When set to `start_over`, it assumes none of the blob is consumed and it will read all blob files from begining.
56
+
57
+ Offsets will be picked up from registry file whenever it exists.
58
+
59
+ __*file_head_bytes*__
60
+
61
+ Specifies the header of the file in bytes that does not repeate over records. Usually, these are json opening tags. The default value is `0`.
62
+
63
+ __*file_tail_bytes*__
64
+
65
+ Specifies the tail of the file that does not repeate over records. Usually, these are json closing tags. The defaul tvalue is `0`.
66
+
67
+ __*record_preprocess_reg_exp*__
68
+
69
+ Specify the regular expression to process content before pushing the event. The matched will be removed. For example, `^\s*,` will removing the leading ',' from the content. The regular expression uses multiline mode.
70
+
71
+ ### Examples
72
+
73
+ * Bare-bone settings:
74
+
75
+ ```yaml
76
+ input
77
+ {
78
+ azureblob
79
+ {
80
+ storage_account_name => "mystorageaccount"
81
+ storage_access_key => "VGhpcyBpcyBhIGZha2Uga2V5Lg=="
82
+ container => "mycontainer"
83
+ }
84
+ }
85
+ ```
86
+
87
+ * Example for Wad-IIS
88
+
89
+ ```yaml
90
+ input {
91
+ azureblob
92
+ {
93
+ storage_account_name => 'mystorageaccount'
94
+ storage_access_key => 'VGhpcyBpcyBhIGZha2Uga2V5Lg=='
95
+ container => 'wad-iis-logfiles'
96
+ codec => line
97
+ }
98
+ }
99
+ filter {
100
+ ## Ignore the comments that IIS will add to the start of the W3C logs
101
+ #
102
+ if [message] =~ "^#" {
103
+ drop {}
104
+ }
105
+
106
+ grok {
107
+ # https://grokdebug.herokuapp.com/
108
+ match => ["message", "%{TIMESTAMP_ISO8601:log_timestamp} %{WORD:sitename} %{WORD:computername} %{IP:server_ip} %{WORD:method} %{URIPATH:uriStem} %{NOTSPACE:uriQuery} %{NUMBER:port} %{NOTSPACE:username} %{IPORHOST:clientIP} %{NOTSPACE:protocolVersion} %{NOTSPACE:userAgent} %{NOTSPACE:cookie} %{NOTSPACE:referer} %{NOTSPACE:requestHost} %{NUMBER:response} %{NUMBER:subresponse} %{NUMBER:win32response} %{NUMBER:bytesSent} %{NUMBER:bytesReceived} %{NUMBER:timetaken}"]
109
+ }
110
+
111
+ ## Set the Event Timesteamp from the log
112
+ #
113
+ date {
114
+ match => [ "log_timestamp", "YYYY-MM-dd HH:mm:ss" ]
115
+ timezone => "Etc/UTC"
116
+ }
117
+
118
+ ## If the log record has a value for 'bytesSent', then add a new field
119
+ # to the event that converts it to kilobytes
120
+ #
121
+ if [bytesSent] {
122
+ ruby {
123
+ code => "event['kilobytesSent'] = event['bytesSent'].to_i / 1024.0"
124
+ }
125
+ }
126
+
127
+ ## Do the same conversion for the bytes received value
128
+ #
129
+ if [bytesReceived] {
130
+ ruby {
131
+ code => "event['kilobytesReceived'] = event['bytesReceived'].to_i / 1024.0"
132
+ }
133
+ }
134
+
135
+ ## Perform some mutations on the records to prep them for Elastic
136
+ #
137
+ mutate {
138
+ ## Convert some fields from strings to integers
139
+ #
140
+ convert => ["bytesSent", "integer"]
141
+ convert => ["bytesReceived", "integer"]
142
+ convert => ["timetaken", "integer"]
143
+
144
+ ## Create a new field for the reverse DNS lookup below
145
+ #
146
+ add_field => { "clientHostname" => "%{clientIP}" }
147
+
148
+ ## Finally remove the original log_timestamp field since the event will
149
+ # have the proper date on it
150
+ #
151
+ remove_field => [ "log_timestamp"]
152
+ }
153
+
154
+ ## Do a reverse lookup on the client IP to get their hostname.
155
+ #
156
+ dns {
157
+ ## Now that we've copied the clientIP into a new field we can
158
+ # simply replace it here using a reverse lookup
159
+ #
160
+ action => "replace"
161
+ reverse => ["clientHostname"]
162
+ }
163
+
164
+ ## Parse out the user agent
165
+ #
166
+ useragent {
167
+ source=> "useragent"
168
+ prefix=> "browser"
169
+ }
170
+ }
171
+ output {
172
+ file {
173
+ path => '/var/tmp/logstash-file-output'
174
+ codec => rubydebug
175
+ }
176
+ stdout {
177
+ codec => rubydebug
178
+ }
179
+ # elasticsearch {
180
+ # hosts => [ "104.45.235.164:9200" ]
181
+ # }
182
+ }
183
+ ```
184
+
185
+ * NSG Logs
186
+
187
+ ```yaml
188
+ input {
189
+ azureblob
190
+ {
191
+ storage_account_name => "mystorageaccount"
192
+ storage_access_key => "VGhpcyBpcyBhIGZha2Uga2V5Lg=="
193
+ container => "insights-logs-networksecuritygroupflowevent"
194
+ codec => "json"
195
+ file_head_bytes => 21
196
+ file_tail_bytes => 8
197
+ record_preprocess_reg_exp => "^\s*,"
198
+ }
199
+ }
200
+
201
+ filter {
202
+ split { field => "[records]" }
203
+ split { field => "[records][properties][flows]"}
204
+ split { field => "[records][properties][flows][flows]"}
205
+ split { field => "[records][properties][flows][flows][flowTuples]"}
206
+
207
+ mutate{
208
+ split => { "[records][resourceId]" => "/"}
209
+ add_field => {"Subscription" => "%{[records][resourceId][2]}"
210
+ "ResourceGroup" => "%{[records][resourceId][4]}"
211
+ "NetworkSecurityGroup" => "%{[records][resourceId][8]}"}
212
+ convert => {"Subscription" => "string"}
213
+ convert => {"ResourceGroup" => "string"}
214
+ convert => {"NetworkSecurityGroup" => "string"}
215
+ split => { "[records][properties][flows][flows][flowTuples]" => ","}
216
+ add_field => {
217
+ "unixtimestamp" => "%{[records][properties][flows][flows][flowTuples][0]}"
218
+ "srcIp" => "%{[records][properties][flows][flows][flowTuples][1]}"
219
+ "destIp" => "%{[records][properties][flows][flows][flowTuples][2]}"
220
+ "srcPort" => "%{[records][properties][flows][flows][flowTuples][3]}"
221
+ "destPort" => "%{[records][properties][flows][flows][flowTuples][4]}"
222
+ "protocol" => "%{[records][properties][flows][flows][flowTuples][5]}"
223
+ "trafficflow" => "%{[records][properties][flows][flows][flowTuples][6]}"
224
+ "traffic" => "%{[records][properties][flows][flows][flowTuples][7]}"
225
+ }
226
+ convert => {"unixtimestamp" => "integer"}
227
+ convert => {"srcPort" => "integer"}
228
+ convert => {"destPort" => "integer"}
229
+ }
230
+
231
+ date{
232
+ match => ["unixtimestamp" , "UNIX"]
233
+ }
234
+ }
235
+
236
+ output {
237
+ stdout { codec => rubydebug }
238
+ }
239
+ ```
240
+
241
+ ## More information
242
+ The source code of this plugin is hosted in GitHub repo [Microsoft Azure Diagnostics with ELK](https://github.com/Azure/azure-diagnostics-tools). We welcome you to provide feedback and/or contribute to the project.
@@ -0,0 +1,376 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
4
+
5
+ # Azure Storage SDK for Ruby
6
+ require "azure/storage"
7
+ require 'json' # for registry content
8
+ require "securerandom" # for generating uuid.
9
+
10
+ # Registry item to coordinate between mulitple clients
11
+ class LogStash::Inputs::RegistryItem
12
+ attr_accessor :file_path, :etag, :offset, :reader, :gen
13
+ # Allow json serialization.
14
+ def as_json(options={})
15
+ {
16
+ file_path: @file_path,
17
+ etag: @etag,
18
+ reader: @reader,
19
+ offset: @offset,
20
+ gen: @gen
21
+ }
22
+ end # as_json
23
+
24
+ def to_json(*options)
25
+ as_json(*options).to_json(*options)
26
+ end # to_json
27
+
28
+ def initialize(file_path, etag, reader, offset = 0, gen = 0)
29
+ @file_path = file_path
30
+ @etag = etag
31
+ @reader = reader
32
+ @offset = offset
33
+ @gen = gen
34
+ end # initialize
35
+ end # class RegistryItem
36
+
37
+
38
+ # Logstash input plugin for Azure Blobs
39
+ #
40
+ # This logstash plugin gathers data from Microsoft Azure Blobs
41
+ class LogStash::Inputs::LogstashInputAzureblob < LogStash::Inputs::Base
42
+ config_name "azureblob"
43
+
44
+ # If undefined, Logstash will complain, even if codec is unused.
45
+ default :codec, "json_lines"
46
+
47
+ # Set the account name for the azure storage account.
48
+ config :storage_account_name, :validate => :string
49
+
50
+ # Set the key to access the storage account.
51
+ config :storage_access_key, :validate => :string
52
+
53
+ # Set the container of the blobs.
54
+ config :container, :validate => :string
55
+
56
+ # Set the endpoint for the blobs.
57
+ #
58
+ # The default, `core.windows.net` targets the public azure.
59
+ config :endpoint, :validate => :string, :default => 'core.windows.net'
60
+
61
+ # Set the value of using backup mode.
62
+ config :backupmode, :validate => :boolean, :default => false, :deprecated => true, :obsolete => 'This option is obsoleted and the settings will be ignored.'
63
+
64
+ # Set the value for the registry file.
65
+ #
66
+ # The default, `data/registry`, is used to coordinate readings for various instances of the clients.
67
+ config :registry_path, :validate => :string, :default => 'data/registry'
68
+
69
+ # Set how many seconds to keep idle before checking for new logs.
70
+ #
71
+ # The default, `30`, means trigger a reading for the log every 30 seconds after entering idle.
72
+ config :interval, :validate => :number, :default => 30
73
+
74
+ # Set the registry create mode
75
+ #
76
+ # The default, `resume`, means when the registry is initially created, it assumes all logs has been handled.
77
+ # When set to `start_over`, it will read all log files from begining.
78
+ config :registry_create_policy, :validate => :string, :default => 'resume'
79
+
80
+ # Set the header of the file that does not repeate over records. Usually, these are json opening tags.
81
+ config :file_head_bytes, :validate => :number, :default => 0
82
+
83
+ # Set the tail of the file that does not repeate over records. Usually, these are json closing tags.
84
+ config :file_tail_bytes, :validate => :number, :default => 0
85
+
86
+ # Set the regular expression to process content before pushing the event.
87
+ config :record_preprocess_reg_exp, :validate => :string
88
+
89
+ # Constant of max integer
90
+ MAX = 2 ** ([42].pack('i').size * 16 -2 ) -1
91
+
92
+ public
93
+ def register
94
+ # this is the reader # for this specific instance.
95
+ @reader = SecureRandom.uuid
96
+ @registry_locker = "#{@registry_path}.lock"
97
+
98
+ # Setup a specific instance of an Azure::Storage::Client
99
+ client = Azure::Storage::Client.create(:storage_account_name => @storage_account_name, :storage_access_key => @storage_access_key, :storage_blob_host => "https://#{@storage_account_name}.blob.#{@endpoint}")
100
+ # Get an azure storage blob service object from a specific instance of an Azure::Storage::Client
101
+ @azure_blob = client.blob_client
102
+ # Add retry filter to the service object
103
+ @azure_blob.with_filter(Azure::Storage::Core::Filter::ExponentialRetryPolicyFilter.new)
104
+ end # def register
105
+
106
+ def run(queue)
107
+ # we can abort the loop if stop? becomes true
108
+ while !stop?
109
+ process(queue)
110
+ Stud.stoppable_sleep(@interval) { stop? }
111
+ end # loop
112
+ end # def run
113
+
114
+ def stop
115
+ cleanup_registry
116
+ end # def stop
117
+
118
+ # Start processing the next item.
119
+ def process(queue)
120
+ begin
121
+ blob, start_index, gen = register_for_read
122
+
123
+ if(!blob.nil?)
124
+ begin
125
+ blob_name = blob.name
126
+ # Work-around: After returned by get_blob, the etag will contains quotes.
127
+ new_etag = blob.properties[:etag]
128
+ # ~ Work-around
129
+
130
+ blob, header = @azure_blob.get_blob(@container, blob_name, {:end_range => @file_head_bytes}) if header.nil? unless @file_head_bytes.nil? or @file_head_bytes <= 0
131
+
132
+ if start_index == 0
133
+ # Skip the header since it is already read for the first read;
134
+ start_index = start_index + @file_head_bytes
135
+ else
136
+ # Adjust the offset when it is second + read til the end of the file, including the tail part.
137
+ start_index = start_index - @file_tail_bytes
138
+ start_index = 0 if start_index < 0
139
+ end
140
+
141
+ blob, content = @azure_blob.get_blob(@container, blob_name, {:start_range => start_index} )
142
+
143
+ # contnet will be used to calculate the new offset. Create a new variable for processed content.
144
+ processed_content = content
145
+ if(!@record_preprocess_reg_exp.nil?)
146
+ reg_exp = Regexp.new(@record_preprocess_reg_exp, Regexp::MULTILINE)
147
+ processed_content = content.sub(reg_exp, '')
148
+ end
149
+
150
+ # Putting header and content and tail together before pushing into event queue
151
+ processed_content = "#{header}#{processed_content}" unless header.nil? || header.length == 0
152
+
153
+ @codec.decode(processed_content) do |event|
154
+ decorate(event)
155
+ queue << event
156
+ end # decode
157
+ ensure
158
+ # Making sure the reader is removed from the registry even when there's exception.
159
+ new_offset = start_index
160
+ new_offset = new_offset + content.length unless content.nil?
161
+ new_registry_item = LogStash::Inputs::RegistryItem.new(blob_name, new_etag, nil, new_offset, gen)
162
+ update_registry(new_registry_item)
163
+ end # begin
164
+ end # if
165
+ rescue StandardError => e
166
+ @logger.error("Oh My, An error occurred. \nError:#{e}:\nTrace:\n#{e.backtrace}", :exception => e)
167
+ end # begin
168
+ end # process
169
+
170
+ # Deserialize registry hash from json string.
171
+ def deserialize_registry_hash (json_string)
172
+ result = Hash.new
173
+ temp_hash = JSON.parse(json_string)
174
+ temp_hash.values.each { |kvp|
175
+ result[kvp['file_path']] = LogStash::Inputs::RegistryItem.new(kvp['file_path'], kvp['etag'], kvp['reader'], kvp['offset'], kvp['gen'])
176
+ }
177
+ return result
178
+ end #deserialize_registry_hash
179
+
180
+ # List all the blobs in the given container.
181
+ def list_all_blobs
182
+ blobs = Set.new []
183
+ continuation_token = NIL
184
+ loop do
185
+ entries = @azure_blob.list_blobs(@container, { :timeout => 10, :marker => continuation_token})
186
+ entries.each do |entry|
187
+ blobs << entry
188
+ end # each
189
+ continuation_token = entries.continuation_token
190
+ break if continuation_token.empty?
191
+ end # loop
192
+ return blobs
193
+ end # def list_blobs
194
+
195
+ # Raise generation for blob in registry
196
+ def raise_gen(registry_hash, file_path)
197
+ begin
198
+ target_item = registry_hash[file_path]
199
+ begin
200
+ target_item.gen += 1
201
+ # Protect gen from overflow.
202
+ target_item.gen = target_item.gen / 2 if target_item.gen == MAX
203
+ rescue StandardError => e
204
+ @logger.error("Fail to get the next generation for target item #{target_item}.", :exception => e)
205
+ target_item.gen = 0
206
+ end
207
+
208
+ min_gen_item = registry_hash.values.min_by { |x| x.gen }
209
+ while min_gen_item.gen > 0
210
+ registry_hash.values.each { |value|
211
+ value.gen -= 1
212
+ }
213
+ min_gen_item = registry_hash.values.min_by { |x| x.gen }
214
+ end
215
+ end
216
+ end # raise_gen
217
+
218
+ # Acquire a lease on a blob item with retries.
219
+ #
220
+ # By default, it will retry 30 times with 1 second interval.
221
+ def acquire_lease(blob_name, retry_times = 30, interval_sec = 1)
222
+ lease = nil;
223
+ retried = 0;
224
+ while lease.nil? do
225
+ begin
226
+ lease = @azure_blob.acquire_blob_lease(@container, blob_name, {:timeout => 10})
227
+ rescue StandardError => e
228
+ if(e.type == 'LeaseAlreadyPresent')
229
+ if (retried > retry_times)
230
+ raise
231
+ end
232
+ retried += 1
233
+ sleep interval_sec
234
+ end
235
+ end
236
+ end #while
237
+ return lease
238
+ end # acquire_lease
239
+
240
+ # Return the next blob for reading as well as the start index.
241
+ def register_for_read
242
+ begin
243
+ all_blobs = list_all_blobs
244
+ registry = all_blobs.find { |item| item.name.downcase == @registry_path }
245
+ registry_locker = all_blobs.find { |item| item.name.downcase == @registry_locker }
246
+
247
+ candidate_blobs = all_blobs.select { |item| (item.name.downcase != @registry_path) && ( item.name.downcase != @registry_locker ) }
248
+
249
+ start_index = 0
250
+ gen = 0
251
+ lease = nil
252
+
253
+ # Put lease on locker file than the registy file to allow update of the registry as a workaround for Azure Storage Ruby SDK issue # 16.
254
+ # Workaround: https://github.com/Azure/azure-storage-ruby/issues/16
255
+ registry_locker = @azure_blob.create_block_blob(@container, @registry_locker, @reader) if registry_locker.nil?
256
+ lease = acquire_lease(@registry_locker)
257
+ # ~ Workaround
258
+
259
+ if(registry.nil?)
260
+ registry_hash = create_registry(candidate_blobs)
261
+ else
262
+ registry_hash = load_registry
263
+ end #if
264
+
265
+ picked_blobs = Set.new []
266
+ # Pick up the next candidate
267
+ picked_blob = nil
268
+ candidate_blobs.each { |candidate_blob|
269
+ registry_item = registry_hash[candidate_blob.name]
270
+
271
+ # Appending items that doesn't exist in the hash table
272
+ if registry_item.nil?
273
+ registry_item = LogStash::Inputs::RegistryItem.new(candidate_blob.name, candidate_blob.properties[:etag], nil, 0, 0)
274
+ registry_hash[candidate_blob.name] = registry_item
275
+ end # if
276
+
277
+ if ((registry_item.offset < candidate_blob.properties[:content_length]) && (registry_item.reader.nil? || registry_item.reader == @reader))
278
+ picked_blobs << candidate_blob
279
+ end
280
+ }
281
+
282
+ picked_blob = picked_blobs.min_by { |b| registry_hash[b.name].gen }
283
+ if !picked_blob.nil?
284
+ registry_item = registry_hash[picked_blob.name]
285
+ registry_item.reader = @reader
286
+ registry_hash[picked_blob.name] = registry_item
287
+ start_index = registry_item.offset
288
+ raise_gen(registry_hash, picked_blob.name)
289
+ gen = registry_item.gen
290
+ end #if
291
+
292
+ # Save the chnage for the registry
293
+ save_registry(registry_hash)
294
+
295
+ @azure_blob.release_blob_lease(@container, @registry_locker, lease)
296
+ lease = nil;
297
+
298
+ return picked_blob, start_index, gen
299
+ rescue StandardError => e
300
+ @logger.error("Oh My, An error occurred. #{e}:\n#{e.backtrace}", :exception => e)
301
+ return nil, nil, nil
302
+ ensure
303
+ @azure_blob.release_blob_lease(@container, @registry_locker, lease) unless lease.nil?
304
+ lease = nil
305
+ end # rescue
306
+ end #register_for_read
307
+
308
+ # Update the registry
309
+ def update_registry (registry_item)
310
+ begin
311
+ lease = nil
312
+ lease = acquire_lease(@registry_locker)
313
+ registry_hash = load_registry
314
+ registry_hash[registry_item.file_path] = registry_item
315
+ save_registry(registry_hash)
316
+ @azure_blob.release_blob_lease(@container, @registry_locker, lease)
317
+ lease = nil
318
+ rescue StandardError => e
319
+ @logger.error("Oh My, An error occurred. #{e}:\n#{e.backtrace}", :exception => e)
320
+ ensure
321
+ @azure_blob.release_blob_lease(@container, @registry_locker, lease) unless lease.nil?
322
+ lease = nil
323
+ end #rescue
324
+ end # def update_registry
325
+
326
+ # Clean up the registry.
327
+ def cleanup_registry
328
+ begin
329
+ lease = nil
330
+ lease = acquire_lease(@registry_locker)
331
+ registry_hash = load_registry
332
+ registry_hash.each { | key, registry_item|
333
+ registry_item.reader = nil if registry_item.reader == @reader
334
+ }
335
+ save_registry(registry_hash)
336
+ @azure_blob.release_blob_lease(@container, @registry_locker, lease)
337
+ lease = nil
338
+ rescue StandardError => e
339
+ @logger.error("Oh My, An error occurred. #{e}:\n#{e.backtrace}", :exception => e)
340
+ ensure
341
+ @azure_blob.release_blob_lease(@container, @registry_locker, lease) unless lease.nil?
342
+ lease = nil
343
+ end #rescue
344
+ end # def cleanup_registry
345
+
346
+ # Create a registry file to coordinate between multiple azure blob inputs.
347
+ def create_registry (blob_items)
348
+ registry_hash = Hash.new
349
+
350
+ blob_items.each do |blob_item|
351
+ initial_offset = 0
352
+ initial_offset = blob_item.properties[:content_length] if @registry_create_policy == 'resume'
353
+ registry_item = LogStash::Inputs::RegistryItem.new(blob_item.name, blob_item.properties[:etag], nil, initial_offset, 0)
354
+ registry_hash[blob_item.name] = registry_item
355
+ end # each
356
+ save_registry(registry_hash)
357
+ return registry_hash
358
+ end # create_registry
359
+
360
+ # Load the content of the registry into the registry hash and return it.
361
+ def load_registry
362
+ # Get content
363
+ registry_blob, registry_blob_body = @azure_blob.get_blob(@container, @registry_path)
364
+ registry_hash = deserialize_registry_hash(registry_blob_body)
365
+ return registry_hash
366
+ end # def load_registry
367
+
368
+ # Serialize the registry hash and save it.
369
+ def save_registry(registry_hash)
370
+ # Serialize hash to json
371
+ registry_hash_json = JSON.generate(registry_hash)
372
+
373
+ # Upload registry to blob
374
+ @azure_blob.create_block_blob(@container, @registry_path, registry_hash_json)
375
+ end # def save_registry
376
+ end # class LogStash::Inputs::LogstashInputAzureblob
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-input-azureblob-saars'
3
+ s.version = '0.9.9'
4
+ s.licenses = ['Apache License (2.0)']
5
+ s.summary = 'This plugin collects Microsoft Azure Diagnostics data from Azure Storage Blobs.'
6
+ s.description = 'This gem is a Logstash plugin. It reads and parses data from Azure Storage Blobs.'
7
+ s.homepage = 'https://github.com/Azure/azure-diagnostics-tools'
8
+ s.authors = ['Microsoft Corporation']
9
+ s.email = 'azdiag@microsoft.com'
10
+ s.require_paths = ['lib']
11
+
12
+ # Files
13
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','Gemfile','LICENSE']
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
19
+
20
+ # Gem dependencies
21
+ s.add_runtime_dependency "logstash-core-plugin-api", '>= 1.60', '<= 2.99'
22
+ s.add_runtime_dependency 'logstash-codec-json_lines'
23
+ s.add_runtime_dependency 'stud', '>= 0.0.22'
24
+ s.add_runtime_dependency 'azure-storage', '~> 0.12.3.preview'
25
+ s.add_development_dependency 'logstash-devutils'
26
+ end
@@ -0,0 +1 @@
1
+ require "logstash/devutils/rspec/spec_helper"
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-azureblob-saars
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.9
5
+ platform: ruby
6
+ authors:
7
+ - Microsoft Corporation
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.60'
19
+ - - "<="
20
+ - !ruby/object:Gem::Version
21
+ version: '2.99'
22
+ name: logstash-core-plugin-api
23
+ prerelease: false
24
+ type: :runtime
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.60'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.99'
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ name: logstash-codec-json_lines
40
+ prerelease: false
41
+ type: :runtime
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.0.22
53
+ name: stud
54
+ prerelease: false
55
+ type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 0.0.22
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: 0.12.3.preview
67
+ name: azure-storage
68
+ prerelease: false
69
+ type: :runtime
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 0.12.3.preview
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ name: logstash-devutils
82
+ prerelease: false
83
+ type: :development
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: This gem is a Logstash plugin. It reads and parses data from Azure Storage Blobs.
90
+ email: azdiag@microsoft.com
91
+ executables: []
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - CHANGELOG.md
96
+ - Gemfile
97
+ - LICENSE
98
+ - README.md
99
+ - lib/logstash/inputs/azureblob.rb
100
+ - logstash-input-azureblob.gemspec
101
+ - spec/inputs/azureblob_spec.rb
102
+ homepage: https://github.com/Azure/azure-diagnostics-tools
103
+ licenses:
104
+ - Apache License (2.0)
105
+ metadata:
106
+ logstash_plugin: 'true'
107
+ logstash_group: input
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.4.8
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: This plugin collects Microsoft Azure Diagnostics data from Azure Storage Blobs.
128
+ test_files:
129
+ - spec/inputs/azureblob_spec.rb