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.
- checksums.yaml +4 -4
- data/README.md +10 -2
- data/lib/logstash/outputs/application_insights.rb +13 -5
- data/lib/logstash/outputs/application_insights/blob.rb +27 -381
- data/lib/logstash/outputs/application_insights/block.rb +28 -21
- data/lib/logstash/outputs/application_insights/channel.rb +143 -48
- data/lib/logstash/outputs/application_insights/channels.rb +4 -3
- data/lib/logstash/outputs/application_insights/clients.rb +1 -1
- data/lib/logstash/outputs/application_insights/config.rb +3 -2
- data/lib/logstash/outputs/application_insights/constants.rb +9 -5
- data/lib/logstash/outputs/application_insights/context.rb +97 -0
- data/lib/logstash/outputs/application_insights/local_file.rb +113 -0
- data/lib/logstash/outputs/application_insights/notification.rb +116 -0
- data/lib/logstash/outputs/application_insights/notification_recovery.rb +5 -6
- data/lib/logstash/outputs/application_insights/shutdown_recovery.rb +3 -2
- data/lib/logstash/outputs/application_insights/state_table.rb +108 -0
- data/lib/logstash/outputs/application_insights/storage_cleanup.rb +4 -3
- data/lib/logstash/outputs/application_insights/storage_recovery.rb +10 -3
- data/lib/logstash/outputs/application_insights/test_notification.rb +3 -6
- data/lib/logstash/outputs/application_insights/test_storage.rb +1 -1
- data/lib/logstash/outputs/application_insights/upload_pipe.rb +285 -0
- data/lib/logstash/outputs/application_insights/validate_notification.rb +1 -1
- data/lib/logstash/outputs/application_insights/validate_storage.rb +1 -1
- data/lib/logstash/outputs/application_insights/version.rb +1 -1
- data/logstash-output-application-insights.gemspec +1 -1
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3218d55e7606dde93f2414ddaf149afc7793c2ad
|
4
|
+
data.tar.gz: 51b48f9e2438b62892070a4a1fc729d59eed4404
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8795709d9c9d9ab0a33b5b2298dfb0d8259cf7c1d10849cbc7074eff14f8d6915b868ca8e9832e17e5aeffb5d1739ac104d2e3fffb6d4f710ca8728117cd5650
|
7
|
+
data.tar.gz: 4eaa0df1d5fdf204c667f848c028e4daaa24ed4311bb936c9194f848a5a98362a72cc3b8aa9e70dfadd4fe3dd885ff525dea48d6c34e4d3ea52ff8eed8e6ad37
|
data/README.md
CHANGED
@@ -185,7 +185,7 @@ case_insensitive_columns => true
|
|
185
185
|
```
|
186
186
|
|
187
187
|
### blob_max_bytesize
|
188
|
-
Advanced, internal, should not be set. Default
|
188
|
+
Advanced, internal, should not be set. Default 4 GB.
|
189
189
|
Azure storage maximum bytesize is 192 GB ( = 50,000 * 4 MB )
|
190
190
|
example:
|
191
191
|
```ruby
|
@@ -193,7 +193,7 @@ blob_max_bytesize => 4000000000
|
|
193
193
|
```
|
194
194
|
|
195
195
|
### blob_max_events
|
196
|
-
Specifies, maximum number of events in one blob. Default
|
196
|
+
Specifies, maximum number of events in one blob. Default 1,000,000 events
|
197
197
|
Setting it too low may improve latency, but will reduce ingestion performance
|
198
198
|
Setting it too high may damage latency up to maximum delay, but ingestion will be more efficient, and load on network will be lower
|
199
199
|
example:
|
@@ -359,12 +359,20 @@ example:
|
|
359
359
|
disable_cleanup => true
|
360
360
|
```
|
361
361
|
|
362
|
+
### disable_compression
|
363
|
+
When set to true, blobs won't be compressed (beware: it will require more storage, more memory and more bandwidth) Default false
|
364
|
+
example:
|
365
|
+
```ruby
|
366
|
+
disable_compression => true
|
367
|
+
```
|
368
|
+
|
362
369
|
### delete_not_notified_blobs
|
363
370
|
When set to true, not notified blobs are deleted, if not set they are copied to the orphan-blobs container. Default false
|
364
371
|
example:
|
365
372
|
```ruby
|
366
373
|
delete_not_notified_blobs => true
|
367
374
|
```
|
375
|
+
|
368
376
|
### validate_notification
|
369
377
|
When set to true, access to application insights will be validated at initialization and if validation fail, logstash process will abort. Default false
|
370
378
|
example:
|
@@ -44,14 +44,21 @@ require "concurrent" # for atomic and thread safe operations
|
|
44
44
|
require "logger"
|
45
45
|
require "csv"
|
46
46
|
|
47
|
+
require "zlib"
|
48
|
+
|
47
49
|
require "application_insights"
|
48
50
|
|
49
51
|
class LogStash::Outputs::Application_insights < LogStash::Outputs::Base
|
50
52
|
require "logstash/outputs/application_insights/version"
|
51
|
-
|
52
|
-
|
53
|
-
require "logstash/outputs/application_insights/
|
54
|
-
require "logstash/outputs/application_insights/
|
53
|
+
autoload :Context, "logstash/outputs/application_insights/context"
|
54
|
+
autoload :Local_file, "logstash/outputs/application_insights/local_file"
|
55
|
+
require "logstash/outputs/application_insights/utils"
|
56
|
+
require "logstash/outputs/application_insights/constants"
|
57
|
+
require "logstash/outputs/application_insights/config"
|
58
|
+
require "logstash/outputs/application_insights/blob"
|
59
|
+
require "logstash/outputs/application_insights/notification"
|
60
|
+
require "logstash/outputs/application_insights/upload_pipe"
|
61
|
+
|
55
62
|
autoload :Block, "logstash/outputs/application_insights/block"
|
56
63
|
autoload :Storage_cleanup, "logstash/outputs/application_insights/storage_cleanup"
|
57
64
|
autoload :Shutdown_recovery, "logstash/outputs/application_insights/shutdown_recovery"
|
@@ -296,6 +303,8 @@ class LogStash::Outputs::Application_insights < LogStash::Outputs::Base
|
|
296
303
|
# and if validation fail, logstash process will abort
|
297
304
|
config :validate_storage, :validate => :boolean
|
298
305
|
|
306
|
+
# When set to true, blobs won't be compressed.
|
307
|
+
config :disable_compression, :validate => :boolean
|
299
308
|
public
|
300
309
|
|
301
310
|
def register
|
@@ -356,7 +365,6 @@ class LogStash::Outputs::Application_insights < LogStash::Outputs::Base
|
|
356
365
|
|
357
366
|
@telemetry.track_event { { :name => "register", :properties => masked_configuration } }
|
358
367
|
|
359
|
-
|
360
368
|
return "ok\n"
|
361
369
|
end # def register
|
362
370
|
|
@@ -19,18 +19,7 @@
|
|
19
19
|
# permissions and limitations under the License.
|
20
20
|
# ----------------------------------------------------------------------------------
|
21
21
|
class LogStash::Outputs::Application_insights
|
22
|
-
class Blob
|
23
|
-
|
24
|
-
attr_reader :instrumentation_key
|
25
|
-
attr_reader :table_id
|
26
|
-
attr_reader :storage_account_name
|
27
|
-
attr_reader :container_name
|
28
|
-
attr_reader :blob_name
|
29
|
-
attr_reader :uploaded_events_count
|
30
|
-
attr_reader :uploaded_bytesize
|
31
|
-
attr_reader :oldest_event_time
|
32
|
-
|
33
|
-
attr_reader :io_queue
|
22
|
+
class Blob < Context
|
34
23
|
|
35
24
|
attr_reader :last_io_exception
|
36
25
|
|
@@ -46,230 +35,38 @@ class LogStash::Outputs::Application_insights
|
|
46
35
|
@@closing
|
47
36
|
end
|
48
37
|
|
49
|
-
def initialize (
|
38
|
+
def initialize ( tuple = nil )
|
50
39
|
@configuration = Config.current
|
51
40
|
@logger = @configuration[:logger]
|
52
41
|
@storage_recovery = Storage_recovery.instance
|
53
42
|
@notification_recovery = Notification_recovery.instance
|
54
43
|
@max_tries = @configuration[:io_max_retries] + 1
|
55
44
|
|
56
|
-
|
57
|
-
@uploaded_block_numbers = [ ]
|
58
|
-
@uploaded_bytesize = 0
|
59
|
-
@uploaded_events_count = 0
|
60
|
-
@sub_state = :none
|
61
|
-
|
62
|
-
if channel
|
63
|
-
@id = id
|
64
|
-
@instrumentation_key = channel.instrumentation_key
|
65
|
-
@table_id = channel.table_id
|
66
|
-
@blob_max_delay = channel.blob_max_delay
|
67
|
-
|
68
|
-
@event_format_ext = channel.event_format_ext
|
69
|
-
|
70
|
-
unless no_queue
|
71
|
-
|
72
|
-
@io_queue = Queue.new
|
73
|
-
@timer = Timer.new
|
74
|
-
|
75
|
-
# create a thread that handles the IO of the blob
|
76
|
-
Thread.new do
|
77
|
-
next_block = nil
|
78
|
-
loop do
|
79
|
-
block_to_upload = nil # release reference to resource for GC
|
80
|
-
block_to_upload = next_block || @io_queue.pop
|
81
|
-
next_block = nil
|
82
|
-
|
83
|
-
if :trigger == @timer.state
|
84
|
-
next_block = block_to_upload unless :wakeup == block_to_upload
|
85
|
-
block_to_upload = :timeout
|
86
|
-
to_commit = :commit
|
87
|
-
|
88
|
-
elsif :close == block_to_upload
|
89
|
-
to_commit = :commit
|
90
|
-
|
91
|
-
# ignore :trigger as they are only to casue check timeout
|
92
|
-
elsif :wakeup == block_to_upload # ignore :wakeup
|
93
|
-
next
|
94
|
-
|
95
|
-
else
|
96
|
-
while @io_queue.length > 0
|
97
|
-
next_block = @io_queue.pop
|
98
|
-
next if :wakeup == next_block # ignore :wakeup
|
99
|
-
break if :close == next_block
|
100
|
-
break if blob_full?( next_block )
|
101
|
-
break unless block_to_upload.concat( next_block )
|
102
|
-
next_block = nil
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
unless to_commit
|
107
|
-
@timer.set( block_to_upload.oldest_event_time + @blob_max_delay, nil ) {|object| @io_queue << :wakeup if 0 == @io_queue.length } if blob_empty?
|
108
|
-
upload( block_to_upload )
|
109
|
-
block_to_upload = nil # release reference to resource for GC
|
110
|
-
to_commit = :commit if blob_full?
|
111
|
-
end
|
112
|
-
|
113
|
-
if to_commit
|
114
|
-
commit unless @uploaded_block_ids.empty?
|
115
|
-
to_commit = nil
|
116
|
-
@uploaded_block_ids = [ ]
|
117
|
-
@timer.cancel
|
118
|
-
break if :close == block_to_upload
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
# close blob. It will finish whatever was already on the queue, and if necessary commit
|
129
|
-
# called on shutdown
|
130
|
-
def close
|
131
|
-
@io_queue << :close
|
132
|
-
end
|
133
|
-
|
134
|
-
def blob_full? ( next_block = nil )
|
135
|
-
if next_block
|
136
|
-
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
|
137
|
-
else
|
138
|
-
BLOB_MAX_BLOCKS <= @uploaded_block_ids.length || @configuration[:blob_max_events] <= @uploaded_events_count || @configuration[:blob_max_bytesize] <= @uploaded_bytesize
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def blob_empty?
|
143
|
-
@uploaded_block_ids.empty?
|
144
|
-
end
|
145
|
-
|
146
|
-
def queue_empty?
|
147
|
-
@io_queue.length == 0 if @io_queue
|
148
|
-
end
|
149
|
-
|
150
|
-
|
151
|
-
def queue_size
|
152
|
-
@io_queue.length
|
153
|
-
end
|
154
|
-
|
155
|
-
def clear_state
|
156
|
-
@action = nil
|
157
|
-
@storage_account_name = nil
|
158
|
-
@container_name = nil
|
159
|
-
@blob_name = nil
|
160
|
-
@uploaded_block_ids = [ ]
|
161
|
-
@uploaded_block_numbers = [ ]
|
162
|
-
@uploaded_events_count = 0
|
163
|
-
@uploaded_bytesize = 0
|
164
|
-
@oldest_event_time = nil
|
165
|
-
end
|
166
|
-
|
167
|
-
def table_entity_to_tuple( options = {} )
|
168
|
-
[ options[:start_time.to_s] || Time.now.utc, options[:action.to_s], options[:instrumentation_key.to_s], options[:table_id.to_s],
|
169
|
-
options[:storage_account_name.to_s], options[:container_name.to_s], options[:blob_name.to_s],
|
170
|
-
eval( options[:uploaded_block_ids.to_s] ), eval( options[:uploaded_block_numbers.to_s] ),
|
171
|
-
options[:uploaded_events_count.to_s] || 0, options[:uploaded_bytesize.to_s] || 0, options[:oldest_event_time.to_s] || Time.now.utc,
|
172
|
-
options[:event_format_ext.to_s], options[:blob_max_delay.to_s] || 0,
|
173
|
-
options[:log_state.to_s].to_sym, (options[:sub_state.to_s] || :none).to_sym
|
174
|
-
]
|
175
|
-
end
|
176
|
-
|
177
|
-
def state_to_tuple
|
178
|
-
[ @start_time || Time.now.utc, @action, @instrumentation_key, @table_id,
|
179
|
-
@storage_account_name, @container_name, @blob_name,
|
180
|
-
@uploaded_block_ids, @uploaded_block_numbers,
|
181
|
-
@uploaded_events_count, @uploaded_bytesize, @oldest_event_time,
|
182
|
-
@event_format_ext, @blob_max_delay,
|
183
|
-
@log_state, @sub_state
|
184
|
-
]
|
185
|
-
end
|
186
|
-
|
187
|
-
def tuple_to_state ( tuple )
|
188
|
-
( @start_time, @action, @instrumentation_key, @table_id,
|
189
|
-
@storage_account_name, @container_name, @blob_name,
|
190
|
-
@uploaded_block_ids, @uploaded_block_numbers,
|
191
|
-
@uploaded_events_count, @uploaded_bytesize, @oldest_event_time,
|
192
|
-
@event_format_ext, @blob_max_delay,
|
193
|
-
@log_state, @sub_state) = tuple
|
194
|
-
end
|
195
|
-
|
196
|
-
def state_to_table_entity
|
197
|
-
{ :start_time => @start_time, :instrumentation_key => @instrumentation_key, :table_id => @table_id,
|
198
|
-
:storage_account_name => @storage_account_name, :container_name => @container_name, :blob_name => @blob_name,
|
199
|
-
:uploaded_block_ids => @uploaded_block_ids.to_s, :uploaded_block_numbers => @uploaded_block_numbers.to_s,
|
200
|
-
:uploaded_events_count => @uploaded_events_count, :uploaded_bytesize => @uploaded_bytesize, :oldest_event_time => @oldest_event_time,
|
201
|
-
:log_state => @log_state, :sub_state => @sub_state
|
202
|
-
}
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
def notify_retry_later
|
207
|
-
if :notify_failed_blob_not_accessible == @recovery
|
208
|
-
@sub_state = @recovery
|
209
|
-
@storage_recovery.recover_later( state_to_tuple, :notify, @storage_account_name )
|
210
|
-
elsif :invalid_instrumentation_key == @recovery || :invalid_table_id == @recovery
|
211
|
-
@sub_state = @recovery
|
212
|
-
Channels.instance.channel( @instrumentation_key, @table_id ).failed_on_notify_retry_Q << state_to_tuple
|
213
|
-
|
214
|
-
else
|
215
|
-
if :notify_failed_blob_not_accessible == @sub_state
|
216
|
-
@storage_recovery.recover_later( state_to_tuple, :notify, @storage_account_name )
|
217
|
-
elsif :invalid_instrumentation_key == @sub_state || :invalid_table_id == @sub_state
|
218
|
-
Channels.instance.channel( @instrumentation_key, @table_id ).failed_on_notify_retry_Q << state_to_tuple
|
219
|
-
else
|
220
|
-
@notification_recovery.recover_later( state_to_tuple )
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
# must return whether notification was successful or failed
|
226
|
-
def notify ( tuple = nil )
|
227
|
-
tuple_to_state( tuple ) if tuple
|
228
|
-
@action = :notify
|
229
|
-
@force_client = true # to enable get a client even if all storage_accounts marked dead
|
230
|
-
@recoverable = [ :notify_failed_blob_not_accessible, :io_failure, :service_unavailable ]
|
231
|
-
success = storage_io_block {
|
232
|
-
set_blob_sas_url
|
233
|
-
payload = create_payload
|
234
|
-
@logger.debug { "notification payload: #{payload}" }
|
235
|
-
@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}, blob_sas_url: #{@blob_sas_url}"
|
236
|
-
|
237
|
-
# assume that exceptions can be raised due to this method:
|
238
|
-
post_notification( @client.notifyClient, payload ) unless @configuration[:disable_notification]
|
239
|
-
@log_state = :notified
|
240
|
-
}
|
241
|
-
if success
|
242
|
-
Telemetry.instance.track_event { { :name => "notified", :properties => state_to_table_entity } }
|
243
|
-
state_table_update
|
244
|
-
else
|
245
|
-
notify_retry_later
|
246
|
-
end
|
247
|
-
success
|
45
|
+
super( tuple )
|
248
46
|
end
|
249
47
|
|
250
48
|
CREATE_EXIST_ERRORS = { :container => [ :create_container, :container_exist ], :table => [ :create_table, :table_exist ] }
|
251
|
-
def create_exist_recovery( type, name
|
252
|
-
prev_info = @info
|
49
|
+
def create_exist_recovery( type, name )
|
253
50
|
if CREATE_EXIST_ERRORS[type][0] == @recovery
|
254
|
-
|
51
|
+
@prev_info = @info
|
255
52
|
@info = "create #{type} #{@storage_account_name}/#{name}"
|
256
53
|
|
257
54
|
# assume that exceptions can be raised due to this method:
|
258
55
|
yield name
|
259
56
|
@logger.info { "Successed to #{@info}" }
|
260
|
-
@info = prev_info
|
57
|
+
@info = @prev_info
|
261
58
|
elsif CREATE_EXIST_ERRORS[type][1] == @recovery
|
262
59
|
@logger.info { "Successed (already exist) to #{@info}" }
|
263
|
-
@info = prev_info
|
60
|
+
@info = @prev_info
|
264
61
|
end
|
265
62
|
end
|
266
63
|
|
267
64
|
def create_table_exist_recovery
|
268
|
-
create_exist_recovery( :table ) { |name| @client.tableClient.create_table( name ) }
|
65
|
+
create_exist_recovery( :table, @configuration[:state_table_name] ) { |name| @client.tableClient.create_table( name ) }
|
269
66
|
end
|
270
67
|
|
271
68
|
def create_container_exist_recovery
|
272
|
-
create_exist_recovery( :container ) { |name| @client.blobClient.create_container( name ) }
|
69
|
+
create_exist_recovery( :container, @container_name ) { |name| @client.blobClient.create_container( name ) }
|
273
70
|
end
|
274
71
|
|
275
72
|
# return true on success
|
@@ -282,38 +79,36 @@ class LogStash::Outputs::Application_insights
|
|
282
79
|
if :entity_exist == @recovery
|
283
80
|
raise NotRecoverableError if :uploading == @log_state
|
284
81
|
else
|
285
|
-
entity_values =
|
82
|
+
entity_values = context_to_table_entity
|
286
83
|
entity_values[:PartitionKey] = "#{@configuration[:partition_key_prefix]}-#{@log_state}"
|
287
84
|
entity_values[:RowKey] = @blob_name.gsub("/","_")
|
288
85
|
@client.tableClient.insert_entity( @configuration[:state_table_name], entity_values )
|
289
86
|
end
|
290
87
|
}
|
291
|
-
@storage_recovery.recover_later(
|
88
|
+
@storage_recovery.recover_later( context_to_tuple, :state_table_update, @storage_account_name ) unless success || :uploading == @log_state
|
292
89
|
success
|
293
90
|
end
|
294
91
|
|
295
|
-
def state_table_update
|
296
|
-
tuple_to_state( tuple ) if tuple
|
92
|
+
def state_table_update
|
297
93
|
if :uploading == @log_state
|
298
94
|
state_table_delete
|
299
95
|
elsif :committed == @log_state
|
300
|
-
if state_table_insert && state_table_delete(
|
96
|
+
if state_table_insert && state_table_delete( :uploading )
|
301
97
|
State.instance.dec_pending_commits
|
302
98
|
State.instance.inc_pending_notifications
|
303
99
|
# this is not a recovery, it is actually enqueue to notify
|
304
|
-
@notification_recovery.enqueue(
|
100
|
+
@notification_recovery.enqueue( context_to_tuple )
|
305
101
|
end
|
306
102
|
elsif :notified == @log_state
|
307
|
-
if (!@configuration[:save_notified_blobs_records] || state_table_insert) && state_table_delete(
|
103
|
+
if (!@configuration[:save_notified_blobs_records] || state_table_insert) && state_table_delete( :committed )
|
308
104
|
State.instance.dec_pending_notifications
|
309
105
|
end
|
310
106
|
end
|
311
107
|
end
|
312
108
|
|
313
109
|
|
314
|
-
#
|
315
|
-
def state_table_delete (
|
316
|
-
tuple_to_state( tuple ) if tuple
|
110
|
+
# return true on success
|
111
|
+
def state_table_delete ( state = nil )
|
317
112
|
state ||= @log_state
|
318
113
|
@action = :state_table_delete
|
319
114
|
@recoverable = [ :invalid_storage_key, :io_failure, :service_unavailable, :table_exist, :create_table, :table_busy, :create_resource ]
|
@@ -327,7 +122,7 @@ class LogStash::Outputs::Application_insights
|
|
327
122
|
@client.tableClient.delete_entity( @configuration[:state_table_name], "#{@configuration[:partition_key_prefix]}-#{state}", @blob_name.gsub( "/", "_" ) )
|
328
123
|
end
|
329
124
|
}
|
330
|
-
@storage_recovery.recover_later(
|
125
|
+
@storage_recovery.recover_later( context_to_tuple, :state_table_update, @storage_account_name ) unless success
|
331
126
|
success
|
332
127
|
end
|
333
128
|
|
@@ -349,109 +144,8 @@ class LogStash::Outputs::Application_insights
|
|
349
144
|
entities
|
350
145
|
end
|
351
146
|
|
352
|
-
def commit ( tuple = nil )
|
353
|
-
tuple_to_state( tuple ) if tuple
|
354
|
-
|
355
|
-
unless @uploaded_block_ids.empty?
|
356
|
-
@action = :commit
|
357
|
-
@recoverable = [ :invalid_storage_key, :io_failure, :service_unavailable ]
|
358
|
-
success = storage_io_block {
|
359
|
-
@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}"
|
360
|
-
# assume that exceptions can be raised due to this method:
|
361
|
-
@client.blobClient.commit_blob_blocks( @container_name, @blob_name, @uploaded_block_ids ) unless @configuration[:disable_blob_upload]
|
362
|
-
@log_state = :committed
|
363
|
-
}
|
364
|
-
if success
|
365
|
-
# next stage
|
366
|
-
state_table_update
|
367
|
-
else
|
368
|
-
@storage_recovery.recover_later( state_to_tuple, :commit, @storage_account_name )
|
369
|
-
end
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
147
|
|
374
|
-
def
|
375
|
-
unless @uploaded_block_ids.empty?
|
376
|
-
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}"
|
377
|
-
@logger.error { "Pospone to #{info1} (; retry later, error: #{@last_io_exception.inspect}" }
|
378
|
-
@storage_recovery.recover_later( state_to_tuple, :commit, @storage_account_name )
|
379
|
-
@uploaded_block_ids = [ ]
|
380
|
-
end
|
381
|
-
unless :io_all_dead == @recovery
|
382
|
-
raise UploadRetryError
|
383
|
-
else
|
384
|
-
Channels.instance.channel( @instrumentation_key, @table_id ).failed_on_upload_retry_Q << @block_to_upload
|
385
|
-
@block_to_upload = nil
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
def upload ( block )
|
390
|
-
@storage_account_name = nil if @uploaded_block_ids.empty?
|
391
|
-
@block_to_upload = block
|
392
|
-
block = nil # remove reference for GC
|
393
|
-
exclude_storage_account_names = [ ]
|
394
|
-
begin
|
395
|
-
if @uploaded_block_ids.empty?
|
396
|
-
@log_state = :uploading
|
397
|
-
@uploaded_block_numbers = [ ]
|
398
|
-
@uploaded_bytesize = 0
|
399
|
-
@uploaded_events_count = 0
|
400
|
-
@oldest_event_time = nil
|
401
|
-
|
402
|
-
# remove record of previous upload that failed
|
403
|
-
if @storage_account_name
|
404
|
-
exclude_storage_account_names << @storage_account_name
|
405
|
-
@storage_recovery.recover_later( state_to_tuple, :state_table_update, @storage_account_name )
|
406
|
-
end
|
407
|
-
set_conatainer_and_blob_names
|
408
|
-
@storage_account_name = Clients.instance.get_random_active_storage( exclude_storage_account_names )
|
409
|
-
unless @storage_account_name
|
410
|
-
upload_recover.call( :io_all_dead, nil )
|
411
|
-
return false
|
412
|
-
end
|
413
|
-
raise UploadRetryError unless state_table_insert
|
414
|
-
end
|
415
|
-
|
416
|
-
@action = :upload
|
417
|
-
@block_info = "blocks: #{@block_to_upload.block_numbers}, events: #{@block_to_upload.events_count}, size: #{@block_to_upload.bytes.length}"
|
418
|
-
@info = "#{@action} #{@storage_account_name}/#{@container_name}/#{@blob_name}, #{@block_info}, commitId: [\"#{100001 + @uploaded_block_ids.length}\"]"
|
419
|
-
@recoverable = [ :invalid_storage_key, :invalid_storage_account, :io_failure, :service_unavailable, :container_exist, :create_container ]
|
420
|
-
|
421
|
-
success = storage_io_block {
|
422
|
-
create_container_exist_recovery
|
423
|
-
block_id = "#{100001 + @uploaded_block_ids.length}"
|
424
|
-
|
425
|
-
# assume that exceptions can be raised due to this method:
|
426
|
-
@client.blobClient.put_blob_block( @container_name, @blob_name, block_id, @block_to_upload.bytes ) unless @configuration[:disable_blob_upload]
|
427
|
-
|
428
|
-
# upload success
|
429
|
-
first_block_in_blob = @uploaded_block_ids.empty?
|
430
|
-
@uploaded_block_ids << [ block_id ]
|
431
|
-
@uploaded_block_numbers.concat( @block_to_upload.block_numbers )
|
432
|
-
@uploaded_bytesize += @block_to_upload.bytes.length
|
433
|
-
@uploaded_events_count += @block_to_upload.events_count
|
434
|
-
@oldest_event_time ||= @block_to_upload.oldest_event_time
|
435
|
-
|
436
|
-
# release memory
|
437
|
-
bytesize = @block_to_upload.bytesize
|
438
|
-
@block_to_upload.dispose
|
439
|
-
@block_to_upload = nil
|
440
|
-
State.instance.inc_pending_commits if first_block_in_blob
|
441
|
-
State.instance.dec_upload_bytesize( bytesize )
|
442
|
-
}
|
443
|
-
|
444
|
-
upload_retry_later unless success
|
445
|
-
rescue UploadRetryError
|
446
|
-
@recovery = nil
|
447
|
-
retry
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
def update_commited_or_uncommited_list( table_entity )
|
452
|
-
tuple = table_entity_to_tuple( table_entity )
|
453
|
-
|
454
|
-
tuple_to_state( tuple )
|
148
|
+
def update_commited_or_uncommited_list
|
455
149
|
@action = :list_blob_blocks
|
456
150
|
@recoverable = [ :invalid_storage_key, :io_failure, :service_unavailable, :container_exist, :create_container, :create_blob ]
|
457
151
|
list_blob_blocks = nil
|
@@ -472,21 +166,20 @@ class LogStash::Outputs::Application_insights
|
|
472
166
|
@uploaded_block_ids << [ block.name ]
|
473
167
|
@uploaded_bytesize += block.size
|
474
168
|
end
|
475
|
-
|
169
|
+
if 0 < @file_size && @uploaded_bytesize != @file_size
|
170
|
+
type = :upload_empty
|
171
|
+
else
|
172
|
+
type = ( blocks.empty? || 0 == @uploaded_bytesize ? :upload_empty : blocks[0].type )
|
173
|
+
end
|
476
174
|
|
477
175
|
@log_state = :committed if :committed == type
|
478
|
-
{ type =>
|
176
|
+
{ type => context_to_tuple }
|
479
177
|
else
|
480
178
|
nil
|
481
179
|
end
|
482
180
|
end
|
483
181
|
|
484
182
|
|
485
|
-
def << ( block )
|
486
|
-
@io_queue << block
|
487
|
-
end
|
488
|
-
|
489
|
-
|
490
183
|
private
|
491
184
|
|
492
185
|
|
@@ -509,7 +202,6 @@ class LogStash::Outputs::Application_insights
|
|
509
202
|
@recovery, reason = recover_retry?( e )
|
510
203
|
retry if @recovery || reason.nil?
|
511
204
|
|
512
|
-
puts " +++ recovery: #{@recovery}, reason: #{reason}"
|
513
205
|
|
514
206
|
@recovery = reason
|
515
207
|
@logger.error { "Failed to #{@info} ; retry later, error: #{e.inspect}" }
|
@@ -520,6 +212,7 @@ class LogStash::Outputs::Application_insights
|
|
520
212
|
end
|
521
213
|
end
|
522
214
|
|
215
|
+
|
523
216
|
def error_to_sym ( e )
|
524
217
|
if e.is_a?( Azure::Core::Http::HTTPError )
|
525
218
|
if 404 == e.status_code
|
@@ -623,7 +316,7 @@ class LogStash::Outputs::Application_insights
|
|
623
316
|
raise e if @configuration[:stop_on_unknown_io_errors]
|
624
317
|
end
|
625
318
|
|
626
|
-
return [
|
319
|
+
return [nil, recovery] unless recovery && @recoverable.include?( recovery )
|
627
320
|
|
628
321
|
case recovery
|
629
322
|
when :container_exist, :table_exist, :entity_exist, :create_container, :create_table
|
@@ -678,52 +371,5 @@ class LogStash::Outputs::Application_insights
|
|
678
371
|
|
679
372
|
|
680
373
|
|
681
|
-
def set_conatainer_and_blob_names
|
682
|
-
time_utc = Time.now.utc
|
683
|
-
id = @id.to_s.rjust(4, "0")
|
684
|
-
strtime = time_utc.strftime( "%F" )
|
685
|
-
@container_name = "#{AZURE_STORAGE_CONTAINER_LOGSTASH_PREFIX}#{@configuration[:azure_storage_container_prefix]}-#{strtime}"
|
686
|
-
|
687
|
-
strtime = time_utc.strftime( "%F-%H-%M-%S-%L" )
|
688
|
-
# @blob_name = "#{@configuration[:azure_storage_blob_prefix]}_ikey-#{@instrumentation_key}_table-#{@table_id}_id-#{id}_#{strtime}.#{@event_format_ext}"
|
689
|
-
@blob_name = "#{AZURE_STORAGE_BLOB_LOGSTASH_PREFIX}#{@configuration[:azure_storage_blob_prefix]}/ikey-#{@instrumentation_key}/table-#{@table_id}/#{strtime}_#{id}.#{@event_format_ext}"
|
690
|
-
end
|
691
|
-
|
692
|
-
|
693
|
-
def create_payload
|
694
|
-
notification_hash = {
|
695
|
-
:data => {
|
696
|
-
:baseType => DATA_BASE_TYPE,
|
697
|
-
:baseData => {
|
698
|
-
:ver => BASE_DATA_REQUIRED_VERSION,
|
699
|
-
:blobSasUri => @blob_sas_url.to_s,
|
700
|
-
:sourceName => @table_id,
|
701
|
-
:sourceVersion => @configuration[:notification_version].to_s
|
702
|
-
}
|
703
|
-
},
|
704
|
-
:ver => @configuration[:notification_version],
|
705
|
-
:name => REQUEST_NAME,
|
706
|
-
:time => Time.now.utc.iso8601,
|
707
|
-
:iKey => @instrumentation_key
|
708
|
-
}
|
709
|
-
notification_hash.to_json
|
710
|
-
end
|
711
|
-
|
712
|
-
|
713
|
-
def post_notification ( http_client, body )
|
714
|
-
request = Azure::Core::Http::HttpRequest.new( :post, @configuration[:application_insights_endpoint], { :body => body, :client => http_client } )
|
715
|
-
request.headers['Content-Type'] = 'application/json; charset=utf-8'
|
716
|
-
request.headers['Accept'] = 'application/json'
|
717
|
-
@logger.debug { "send notification : \n endpoint: #{@configuration[:application_insights_endpoint]}\n body : #{body}" }
|
718
|
-
response = request.call
|
719
|
-
end
|
720
|
-
|
721
|
-
|
722
|
-
def set_blob_sas_url
|
723
|
-
blob_url ="https://#{@storage_account_name}.blob.core.windows.net/#{@container_name}/#{@blob_name}"
|
724
|
-
options_and_constrains = {:permissions => "r", :resource => "b", :expiry => ( Time.now.utc + @configuration[:blob_access_expiry_time] ).iso8601 }
|
725
|
-
@blob_sas_url = @client.storage_auth_sas.signed_uri( URI( blob_url ), options_and_constrains )
|
726
|
-
end
|
727
|
-
|
728
374
|
end
|
729
375
|
end
|