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