logstash-output-application_insights 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -2
  3. data/lib/logstash/outputs/application_insights.rb +13 -5
  4. data/lib/logstash/outputs/application_insights/blob.rb +27 -381
  5. data/lib/logstash/outputs/application_insights/block.rb +28 -21
  6. data/lib/logstash/outputs/application_insights/channel.rb +143 -48
  7. data/lib/logstash/outputs/application_insights/channels.rb +4 -3
  8. data/lib/logstash/outputs/application_insights/clients.rb +1 -1
  9. data/lib/logstash/outputs/application_insights/config.rb +3 -2
  10. data/lib/logstash/outputs/application_insights/constants.rb +9 -5
  11. data/lib/logstash/outputs/application_insights/context.rb +97 -0
  12. data/lib/logstash/outputs/application_insights/local_file.rb +113 -0
  13. data/lib/logstash/outputs/application_insights/notification.rb +116 -0
  14. data/lib/logstash/outputs/application_insights/notification_recovery.rb +5 -6
  15. data/lib/logstash/outputs/application_insights/shutdown_recovery.rb +3 -2
  16. data/lib/logstash/outputs/application_insights/state_table.rb +108 -0
  17. data/lib/logstash/outputs/application_insights/storage_cleanup.rb +4 -3
  18. data/lib/logstash/outputs/application_insights/storage_recovery.rb +10 -3
  19. data/lib/logstash/outputs/application_insights/test_notification.rb +3 -6
  20. data/lib/logstash/outputs/application_insights/test_storage.rb +1 -1
  21. data/lib/logstash/outputs/application_insights/upload_pipe.rb +285 -0
  22. data/lib/logstash/outputs/application_insights/validate_notification.rb +1 -1
  23. data/lib/logstash/outputs/application_insights/validate_storage.rb +1 -1
  24. data/lib/logstash/outputs/application_insights/version.rb +1 -1
  25. data/logstash-output-application-insights.gemspec +1 -1
  26. metadata +9 -4
@@ -43,9 +43,9 @@ class LogStash::Outputs::Application_insights
43
43
  # super first parameter must be nil. blob first parameter is channel, otherwise it will pass storage_account_name as channel
44
44
  super( nil )
45
45
  @storage_account_name = storage_account_name
46
- @azure_storage_container_prefix = @configuration[:azure_storage_container_prefix]
46
+ @azure_storage_container_prefix = "#{AZURE_STORAGE_CONTAINER_LOGSTASH_PREFIX}#{@configuration[:azure_storage_container_prefix]}"
47
47
  @retention_time = @configuration[:blob_retention_time] + 24 * 60 * 60
48
- @not_notified_container = "#{@configuration[:azure_storage_container_prefix]}-#{AZURE_STORAGE_ORPHAN_BLOBS_CONTAINER_NAME}"
48
+ @not_notified_container = "#{AZURE_STORAGE_CONTAINER_LOGSTASH_PREFIX}#{@configuration[:azure_storage_container_prefix]}-#{AZURE_STORAGE_ORPHAN_BLOBS_CONTAINER_NAME}"
49
49
  # launch tread that cleans the storage
50
50
  periodic_storage_cleanup
51
51
  end
@@ -179,7 +179,8 @@ class LogStash::Outputs::Application_insights
179
179
  return nil unless entities
180
180
  token = entities.continuation_token
181
181
  entities.each do |entity|
182
- return nil unless state_table_delete( table_entity_to_tuple( entity.properties ) )
182
+ table_entity_to_context( entity.properties )
183
+ return nil unless state_table_delete
183
184
  end
184
185
  end while continuation_token
185
186
  true
@@ -27,6 +27,7 @@ class LogStash::Outputs::Application_insights
27
27
  configuration = Config.current
28
28
  @logger = configuration[:logger]
29
29
  @storage_account_name_key = configuration[:storage_account_name_key]
30
+
30
31
  @queues = { :commit => {}, :notify => {}, :state_table_update => {} }
31
32
  init_queues( @storage_account_name_key, @queues )
32
33
 
@@ -43,7 +44,7 @@ class LogStash::Outputs::Application_insights
43
44
  end
44
45
  end
45
46
 
46
- def recover_later ( tuple, action = nil, storage_account_name = nil )
47
+ def recover_later ( tuple, action , storage_account_name )
47
48
  if stopped?
48
49
  if :commit == action
49
50
  @state ||= State.instance
@@ -87,11 +88,17 @@ class LogStash::Outputs::Application_insights
87
88
  Stud.stoppable_sleep(Float::INFINITY, 1) { ( state_on?( storage_account_name ) || stopped? ) && 10 > counter.value }
88
89
 
89
90
  if stopped? && !state_on?( storage_account_name )
90
- recover_later( tuple )
91
+ recover_later( tuple, action, storage_account_name )
91
92
  else
92
93
  counter.increment
93
94
  Thread.new( action, counter, tuple ) do |action, counter, tuple|
94
- Blob.new.send( action, tuple )
95
+ if :notify == action
96
+ Notification.new( tuple ).notify
97
+ elsif :commit == action
98
+ Upload_pipe.new( nil, nil, tuple ).commit
99
+ elsif :state_table_update == action
100
+ Blob.new( tuple ).state_table_update
101
+ end
95
102
  counter.decrement
96
103
  end
97
104
  end
@@ -19,11 +19,10 @@
19
19
  # permissions and limitations under the License.
20
20
  # ----------------------------------------------------------------------------------
21
21
  class LogStash::Outputs::Application_insights
22
- class Test_notification < Blob
22
+ class Test_notification < Notification
23
23
 
24
24
 
25
25
  def initialize
26
- # super first parameter must be nil. blob first parameter is channel, otherwise it will pass storage_account_name as channel
27
26
  super( nil )
28
27
  @storage_account_name = @configuration[:storage_account_name_key][0][0]
29
28
  @action = :test_notification
@@ -36,13 +35,11 @@ class LogStash::Outputs::Application_insights
36
35
  @instrumentation_key = GUID_NULL
37
36
  end
38
37
 
39
- def submit
38
+ def test
40
39
  @max_tries = 1
41
40
  storage_io_block {
42
41
  if @recovery.nil?
43
- set_blob_sas_url
44
- payload = create_payload
45
- post_notification( @client.notifyClient, payload )
42
+ submit
46
43
  end
47
44
  }
48
45
  end
@@ -32,7 +32,7 @@ class LogStash::Outputs::Application_insights
32
32
  @force_client = true # to enable get a client even if all storage_accounts marked dead
33
33
  end
34
34
 
35
- def submit
35
+ def test
36
36
  @max_tries = 1
37
37
  storage_io_block {
38
38
  if @recovery.nil? || :invalid_storage_key == @recovery
@@ -0,0 +1,285 @@
1
+ # encoding: utf-8
2
+
3
+ # ----------------------------------------------------------------------------------
4
+ # Logstash Output Application Insights
5
+ #
6
+ # Copyright (c) Microsoft Corporation
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Licensed under the Apache License, Version 2.0 (the License);
11
+ # you may not use this file except in compliance with the License.
12
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ #
18
+ # See the Apache Version 2.0 License for specific language governing
19
+ # permissions and limitations under the License.
20
+ # ----------------------------------------------------------------------------------
21
+ class LogStash::Outputs::Application_insights
22
+ class Upload_pipe < Blob
23
+
24
+
25
+ def initialize ( channel = nil, id = nil, tuple = nil )
26
+
27
+ # super first parameter must be nil. blob first parameter is channel, otherwise it will pass storage_account_name as channel
28
+ super( tuple )
29
+ @channel = channel
30
+ if @channel
31
+ @id = id
32
+ @instrumentation_key = @channel.instrumentation_key
33
+ @table_id = @channel.table_id
34
+ @blob_max_delay = @channel.blob_max_delay
35
+ @blob_extension = @channel.blob_extension
36
+ @event_format = @channel.event_format
37
+ @file_pipe = @channel.file_pipe?
38
+
39
+ @io_queue = Queue.new
40
+
41
+ # create a thread that handles the IO of the blob
42
+ if @file_pipe
43
+ launch_file_pipe_thread
44
+ else
45
+ launch_block_pipe_thread
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+
52
+ def launch_block_pipe_thread
53
+ Thread.new do
54
+ timer = Timer.new
55
+ next_block = nil
56
+ loop do
57
+ block_to_upload = nil # release reference to resource for GC
58
+ block_to_upload = next_block || @io_queue.pop
59
+ next_block = nil
60
+
61
+ if :trigger == timer.state
62
+ next_block = block_to_upload unless :wakeup == block_to_upload
63
+ block_to_upload = :timeout
64
+ to_commit = :commit
65
+
66
+ elsif :close == block_to_upload
67
+ to_commit = :commit
68
+
69
+ # ignore :trigger as they are only to casue check timeout
70
+ elsif :wakeup == block_to_upload # ignore :wakeup
71
+ next
72
+
73
+ else
74
+ while @io_queue.length > 0
75
+ next_block = @io_queue.pop
76
+ next if :wakeup == next_block # ignore :wakeup
77
+ break if :close == next_block
78
+ break if blob_full?( next_block )
79
+ break unless block_to_upload.concat( next_block )
80
+ next_block = nil
81
+ end
82
+ end
83
+
84
+ unless to_commit
85
+ timer.set( block_to_upload.oldest_event_time + @blob_max_delay, nil ) {|object| @io_queue << :wakeup if 0 == @io_queue.length } if blob_empty?
86
+ upload( block_to_upload )
87
+ block_to_upload = nil # release reference to resource for GC
88
+ to_commit = :commit if blob_full?
89
+ end
90
+
91
+ if to_commit
92
+ commit unless @uploaded_block_ids.empty?
93
+ to_commit = nil
94
+ @uploaded_block_ids = [ ]
95
+ timer.cancel
96
+ break if :close == block_to_upload
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+
103
+ def launch_file_pipe_thread
104
+ Thread.new do
105
+ loop do
106
+ file_to_upload = @io_queue.pop
107
+
108
+ break if :close == file_to_upload
109
+
110
+ @file_size = nil
111
+ while block = file_to_upload.get_next_block
112
+ @file_size ||= file_to_upload.file_size
113
+ unless upload( block )
114
+ @channel.recover_later_file_upload( file_to_upload )
115
+ file_to_upload = nil
116
+ @uploaded_block_ids = [ ]
117
+ break
118
+ end
119
+ end
120
+ file_to_upload.dispose if file_to_upload
121
+ file_to_upload = nil
122
+
123
+ commit unless @uploaded_block_ids.empty?
124
+ @uploaded_block_ids = [ ]
125
+ end
126
+ end
127
+ end
128
+
129
+
130
+ # close blob. It will finish whatever was already on the queue, and if necessary commit
131
+ # called on shutdown
132
+ def close
133
+ @io_queue << :close
134
+ end
135
+
136
+
137
+ def queue_size
138
+ @io_queue.length
139
+ end
140
+
141
+ def busy?
142
+ 0 < @io_queue.length || 0 == @io_queue.num_waiting
143
+ end
144
+
145
+ def << ( block )
146
+ @io_queue << block
147
+ end
148
+
149
+
150
+ def commit
151
+ unless @uploaded_block_ids.empty?
152
+ @action = :commit
153
+ @recoverable = [ :invalid_storage_key, :io_failure, :service_unavailable ]
154
+ success = storage_io_block {
155
+ @info = "#{@action.to_s} #{@storage_account_name}/#{@container_name}/#{@blob_name}, events: #{@uploaded_events_count}, size: #{@uploaded_bytesize}, blocks: #{@uploaded_block_numbers}, delay: #{Time.now.utc - @oldest_event_time}"
156
+ # assume that exceptions can be raised due to this method:
157
+ @client.blobClient.commit_blob_blocks( @container_name, @blob_name, @uploaded_block_ids ) unless @configuration[:disable_blob_upload]
158
+ @log_state = :committed
159
+ }
160
+ if success
161
+ # next stage
162
+ state_table_update
163
+ else
164
+ @storage_recovery.recover_later( context_to_tuple, :commit, @storage_account_name )
165
+ end
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ def blob_full? ( next_block = nil )
172
+ if next_block
173
+ BLOB_MAX_BLOCKS < @uploaded_block_ids.length + 1 || @configuration[:blob_max_events] < @uploaded_events_count + next_block.events_count || @configuration[:blob_max_bytesize] < @uploaded_bytesize + next_block.bytesize
174
+ else
175
+ BLOB_MAX_BLOCKS <= @uploaded_block_ids.length || @configuration[:blob_max_events] <= @uploaded_events_count || @configuration[:blob_max_bytesize] <= @uploaded_bytesize
176
+ end
177
+ end
178
+
179
+ def blob_empty?
180
+ @uploaded_block_ids.empty?
181
+ end
182
+
183
+
184
+ def upload_retry_later
185
+
186
+ unless @uploaded_block_ids.empty?
187
+ if @file_pipe
188
+ # remove "loading" record from state table of all previous blocks uploaded , we will try the whole file on an alternative storage
189
+ @storage_recovery.recover_later( context_to_tuple, :state_table_update, @storage_account_name )
190
+ @block_to_upload = nil
191
+ return
192
+ else
193
+ info1 = "#{:commit} #{@storage_account_name}/#{@container_name}/#{@blob_name}, events: #{@uploaded_events_count}, size: #{@uploaded_bytesize}, blocks: #{@uploaded_block_numbers}, delay: #{Time.now.utc - @oldest_event_time}"
194
+ @logger.error { "Pospone to #{info1} (; retry later, error: #{@last_io_exception.inspect}" }
195
+ @storage_recovery.recover_later( context_to_tuple, :commit, @storage_account_name )
196
+ @uploaded_block_ids = [ ]
197
+ end
198
+ end
199
+ if :io_all_dead == @recovery
200
+ @channel.recover_later_block_upload( @block_to_upload ) unless @file_pipe
201
+ @block_to_upload = nil
202
+ else
203
+ raise UploadRetryError
204
+ end
205
+ end
206
+
207
+ # returns success / failure
208
+ def upload ( block )
209
+ @storage_account_name = nil if @uploaded_block_ids.empty?
210
+ @block_to_upload = block
211
+ block = nil # remove reference for GC
212
+ exclude_storage_account_names = [ ]
213
+ success = false
214
+ begin
215
+ if @uploaded_block_ids.empty?
216
+ @log_state = :uploading
217
+ @uploaded_block_numbers = [ ]
218
+ @uploaded_bytesize = 0
219
+ @uploaded_events_count = 0
220
+ @oldest_event_time = nil
221
+
222
+ # remove "loading" record from state table of first block upload that failed, we will try on alternative storage
223
+ if @storage_account_name
224
+ exclude_storage_account_names << @storage_account_name
225
+ @storage_recovery.recover_later( context_to_tuple, :state_table_update, @storage_account_name )
226
+ end
227
+ set_conatainer_and_blob_names
228
+ @storage_account_name = Clients.instance.get_random_active_storage( exclude_storage_account_names )
229
+ unless @storage_account_name
230
+ @recovery = :io_all_dead
231
+ upload_retry_later
232
+ return false
233
+ end
234
+ raise UploadRetryError unless state_table_insert
235
+ end
236
+
237
+ @action = :upload
238
+ @block_info = "blocks: #{@block_to_upload.block_numbers}, events: #{@block_to_upload.events_count}, size: #{@block_to_upload.bytes.length}"
239
+ @info = "#{@action} #{@storage_account_name}/#{@container_name}/#{@blob_name}, #{@block_info}, commitId: [\"#{100001 + @uploaded_block_ids.length}\"]"
240
+ @recoverable = [ :invalid_storage_key, :invalid_storage_account, :io_failure, :service_unavailable, :container_exist, :create_container ]
241
+
242
+ success = storage_io_block {
243
+ create_container_exist_recovery
244
+ block_id = "#{100001 + @uploaded_block_ids.length}"
245
+
246
+ # assume that exceptions can be raised due to this method:
247
+ @client.blobClient.put_blob_block( @container_name, @blob_name, block_id, @block_to_upload.bytes ) unless @configuration[:disable_blob_upload]
248
+
249
+ # upload success
250
+ first_block_in_blob = @uploaded_block_ids.empty?
251
+ @uploaded_block_ids << [ block_id ]
252
+ @uploaded_block_numbers.concat( @block_to_upload.block_numbers )
253
+ @uploaded_bytesize += @block_to_upload.bytes.length
254
+ @uploaded_events_count += @block_to_upload.events_count
255
+ @oldest_event_time ||= @block_to_upload.oldest_event_time
256
+
257
+ # release memory
258
+ bytesize = @block_to_upload.bytesize
259
+ @block_to_upload.dispose
260
+ @block_to_upload = nil
261
+ State.instance.inc_pending_commits if first_block_in_blob
262
+ State.instance.dec_upload_bytesize( bytesize )
263
+ }
264
+
265
+ upload_retry_later unless success
266
+ rescue UploadRetryError
267
+ @recovery = nil
268
+ retry
269
+ end
270
+ success
271
+ end
272
+
273
+
274
+ def set_conatainer_and_blob_names
275
+ time_utc = Time.now.utc
276
+ id = @id.to_s.rjust(4, "0")
277
+ strtime = time_utc.strftime( "%F" )
278
+ @container_name = "#{AZURE_STORAGE_CONTAINER_LOGSTASH_PREFIX}#{@configuration[:azure_storage_container_prefix]}-#{strtime}"
279
+
280
+ strtime = time_utc.strftime( "%F-%H-%M-%S-%L" )
281
+ @blob_name = "#{AZURE_STORAGE_BLOB_LOGSTASH_PREFIX}#{@configuration[:azure_storage_blob_prefix]}/ikey-#{@instrumentation_key}/table-#{@table_id}/#{strtime}_#{id}#{@blob_extension}"
282
+ end
283
+
284
+ end
285
+ end
@@ -29,7 +29,7 @@ class LogStash::Outputs::Application_insights
29
29
  end
30
30
 
31
31
  def validate
32
- {:success => @test_notification.submit, :error => @test_notification.last_io_exception }
32
+ {:success => @test_notification.test, :error => @test_notification.last_io_exception }
33
33
  end
34
34
  end
35
35
  end
@@ -33,7 +33,7 @@ class LogStash::Outputs::Application_insights
33
33
  result = {}
34
34
  @storage_account_name_key.each do |storage_account_name, storage_account_keys|
35
35
  test_storage = Test_storage.new( storage_account_name )
36
- result[storage_account_name] = {:success => test_storage.submit, :error => test_storage.last_io_exception }
36
+ result[storage_account_name] = {:success => test_storage.test, :error => test_storage.last_io_exception }
37
37
  end
38
38
  result
39
39
  end
@@ -20,5 +20,5 @@
20
20
  # ----------------------------------------------------------------------------------
21
21
 
22
22
  # class LogStash::Outputs::Application_insights
23
- APPLICATION_INSIGHTS_VERSION ||= "0.1.6"
23
+ APPLICATION_INSIGHTS_VERSION ||= "0.2.0"
24
24
  # end
@@ -42,7 +42,7 @@ Gem::Specification.new do |s|
42
42
  s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
43
43
 
44
44
  # Gem dependencies
45
- s.add_runtime_dependency "logstash-core", ">= 2.0.0", "< 3.0.0"
45
+ s.add_runtime_dependency "logstash-core", ">= 2.0.0", "< 6.0.0"
46
46
  s.add_runtime_dependency "azure-storage", "0.10.1.preview"
47
47
  s.add_runtime_dependency "azure-core", "0.1.2"
48
48
  s.add_runtime_dependency "application_insights", ">= 0.5.3"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-application_insights
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Microsoft Corporation
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-27 00:00:00.000000000 Z
11
+ date: 2016-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -18,7 +18,7 @@ dependencies:
18
18
  version: 2.0.0
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
- version: 3.0.0
21
+ version: 6.0.0
22
22
  name: logstash-core
23
23
  prerelease: false
24
24
  type: :runtime
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 2.0.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 3.0.0
32
+ version: 6.0.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  requirement: !ruby/object:Gem::Requirement
35
35
  requirements:
@@ -108,13 +108,17 @@ files:
108
108
  - lib/logstash/outputs/application_insights/clients.rb
109
109
  - lib/logstash/outputs/application_insights/config.rb
110
110
  - lib/logstash/outputs/application_insights/constants.rb
111
+ - lib/logstash/outputs/application_insights/context.rb
111
112
  - lib/logstash/outputs/application_insights/exceptions.rb
112
113
  - lib/logstash/outputs/application_insights/flow_control.rb
114
+ - lib/logstash/outputs/application_insights/local_file.rb
113
115
  - lib/logstash/outputs/application_insights/multi_io_logger.rb
116
+ - lib/logstash/outputs/application_insights/notification.rb
114
117
  - lib/logstash/outputs/application_insights/notification_recovery.rb
115
118
  - lib/logstash/outputs/application_insights/shutdown.rb
116
119
  - lib/logstash/outputs/application_insights/shutdown_recovery.rb
117
120
  - lib/logstash/outputs/application_insights/state.rb
121
+ - lib/logstash/outputs/application_insights/state_table.rb
118
122
  - lib/logstash/outputs/application_insights/storage_cleanup.rb
119
123
  - lib/logstash/outputs/application_insights/storage_recovery.rb
120
124
  - lib/logstash/outputs/application_insights/sub_channel.rb
@@ -122,6 +126,7 @@ files:
122
126
  - lib/logstash/outputs/application_insights/test_notification.rb
123
127
  - lib/logstash/outputs/application_insights/test_storage.rb
124
128
  - lib/logstash/outputs/application_insights/timer.rb
129
+ - lib/logstash/outputs/application_insights/upload_pipe.rb
125
130
  - lib/logstash/outputs/application_insights/utils.rb
126
131
  - lib/logstash/outputs/application_insights/validate_notification.rb
127
132
  - lib/logstash/outputs/application_insights/validate_storage.rb