logstash-output-application_insights 0.1.6 → 0.2.0

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