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
@@ -73,6 +73,7 @@ class LogStash::Outputs::Application_insights
|
|
73
73
|
:blob_serialization => @blob_serialization || DEFAULT_BLOB_SERIALIZATION,
|
74
74
|
:csv_separator => @csv_separator || DEFAULT_CSV_SEPARATOR,
|
75
75
|
:csv_default_value => @csv_default_value || DEFAULT_CSV_DEFAULT_VALUE,
|
76
|
+
:disable_compression => @disable_compression || DEFAULT_DISABLE_COMPRESSION,
|
76
77
|
|
77
78
|
}
|
78
79
|
end
|
@@ -82,6 +83,7 @@ class LogStash::Outputs::Application_insights
|
|
82
83
|
:disable_cleanup, :delete_not_notified_blobs,
|
83
84
|
:validate_notification, :validate_storage,
|
84
85
|
:save_notified_blobs_records, :case_insensitive_columns,
|
86
|
+
:disable_compression,
|
85
87
|
:table_columns, :serialized_event_field ]
|
86
88
|
|
87
89
|
GUID_NULL = "00000000-0000-0000-0000-000000000000"
|
@@ -140,6 +142,9 @@ class LogStash::Outputs::Application_insights
|
|
140
142
|
MIN_FLOW_CONTROL_DELAY = 0.1 # in seconds, 1 seconds, can be less than 1 seconds, like 0.5, 0.1
|
141
143
|
MAX_FLOW_CONTROL_DELAY = 0 # in seconds, 1 seconds, can be less than 1 seconds, like 0.5, 0.1
|
142
144
|
|
145
|
+
MAX_CHANNEL_UPLOAD_PIPES = 40
|
146
|
+
CHANNEL_THRESHOLD_TO_ADD_UPLOAD_PIPE = 3 # not relevant for file upload mode
|
147
|
+
|
143
148
|
METADATA_FIELD_INSTRUMENTATION_KEY = "[@metadata]instrumentation_key"
|
144
149
|
METADATA_FIELD_TABLE_ID = "[@metadata]table_id"
|
145
150
|
FIELD_INSTRUMENTATION_KEY = "instrumentation_key"
|
@@ -151,6 +156,7 @@ class LogStash::Outputs::Application_insights
|
|
151
156
|
AZURE_STORAGE_CONTAINER_LOGSTASH_PREFIX = "logstash" # lower case only, dash allowed
|
152
157
|
AZURE_STORAGE_BLOB_LOGSTASH_PREFIX = "logstash"
|
153
158
|
AZURE_STORAGE_TABLE_LOGSTASH_PREFIX = "Logstash" # case sensitive, no dash
|
159
|
+
LOCAL_FS_FILE_PREFIX = "application_insights"
|
154
160
|
|
155
161
|
AZURE_STORAGE_ORPHAN_BLOBS_CONTAINER_NAME = "orphan-blobs"
|
156
162
|
AZURE_STORAGE_NOTIFIED_BLOBS_TABLE_NAME = "orphan-blobs"
|
@@ -174,14 +180,11 @@ class LogStash::Outputs::Application_insights
|
|
174
180
|
DEFAULT_AZURE_STORAGE_BLOB_PREFIX = nil
|
175
181
|
DEFAULT_AZURE_STORAGE_CONTAINER_PREFIX = nil
|
176
182
|
DEFAULT_AZURE_STORAGE_TABLE_PREFIX = nil
|
177
|
-
DEFAULT_JSON_EXT = EXT_EVENT_FORMAT_JSON
|
178
|
-
DEFAULT_CSV_EXT = EXT_EVENT_FORMAT_CSV
|
179
|
-
|
180
183
|
|
181
184
|
DEFAULT_BLOB_SERIALIZATION = EXT_EVENT_FORMAT_JSON
|
182
185
|
|
183
|
-
DEFAULT_BLOB_MAX_BYTESIZE =
|
184
|
-
DEFAULT_BLOB_MAX_EVENTS =
|
186
|
+
DEFAULT_BLOB_MAX_BYTESIZE = 4 * 1024 * 1024 * 1024 # 4 Giga bytes
|
187
|
+
DEFAULT_BLOB_MAX_EVENTS = 1000 * 1000 # 1,000,000
|
185
188
|
|
186
189
|
DEFAULT_BLOB_MAX_DELAY = 60 # in seconds
|
187
190
|
DEFAULT_BLOB_RETENTION_TIME = 60 * 60 * 24 * 7 # in seconds - one week
|
@@ -197,6 +200,7 @@ class LogStash::Outputs::Application_insights
|
|
197
200
|
DEFAULT_DISABLE_CLEANUP = false
|
198
201
|
DEFAULT_DELETE_NOT_NOTIFIED_BLOBS = false
|
199
202
|
DEFAULT_SAVE_NOTIFIED_BLOBS_RECORDS = false
|
203
|
+
DEFAULT_DISABLE_COMPRESSION = false
|
200
204
|
|
201
205
|
DEFAULT_CASE_INSENSITIVE = false
|
202
206
|
|
@@ -0,0 +1,97 @@
|
|
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
|
+
|
22
|
+
class LogStash::Outputs::Application_insights
|
23
|
+
class Context
|
24
|
+
|
25
|
+
def initialize ( tuple )
|
26
|
+
if tuple
|
27
|
+
tuple_to_context( tuple )
|
28
|
+
else
|
29
|
+
clear_context
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def clear_context
|
34
|
+
@start_time = Time.now.utc
|
35
|
+
@action = nil
|
36
|
+
@instrumentation_key = nil
|
37
|
+
@table_id = nil
|
38
|
+
@storage_account_name = nil
|
39
|
+
@container_name = nil
|
40
|
+
@blob_name = nil
|
41
|
+
@uploaded_block_ids = [ ]
|
42
|
+
@uploaded_block_numbers = [ ]
|
43
|
+
@uploaded_events_count = 0
|
44
|
+
@uploaded_bytesize = 0
|
45
|
+
@oldest_event_time = nil
|
46
|
+
@event_format = nil
|
47
|
+
@blob_max_delay = nil
|
48
|
+
@log_state = nil
|
49
|
+
@sub_state = :none
|
50
|
+
@file_size = 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def table_entity_to_tuple( options = {} )
|
54
|
+
[ options[:start_time.to_s] || Time.now.utc, options[:action.to_s], options[:instrumentation_key.to_s], options[:table_id.to_s],
|
55
|
+
options[:storage_account_name.to_s], options[:container_name.to_s], options[:blob_name.to_s],
|
56
|
+
eval( options[:uploaded_block_ids.to_s] ), eval( options[:uploaded_block_numbers.to_s] ),
|
57
|
+
options[:uploaded_events_count.to_s] || 0, options[:uploaded_bytesize.to_s] || 0, options[:oldest_event_time.to_s] || Time.now.utc,
|
58
|
+
options[:event_format.to_s], options[:blob_max_delay.to_s] || 0,
|
59
|
+
options[:log_state.to_s].to_sym, (options[:sub_state.to_s] || :none).to_sym, options[:file_size.to_s] || 0
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
def table_entity_to_context ( table_entity )
|
64
|
+
tuple_to_context( table_entity_to_tuple( table_entity ) )
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def context_to_tuple
|
69
|
+
[ @start_time || Time.now.utc, @action, @instrumentation_key, @table_id,
|
70
|
+
@storage_account_name, @container_name, @blob_name,
|
71
|
+
@uploaded_block_ids, @uploaded_block_numbers,
|
72
|
+
@uploaded_events_count, @uploaded_bytesize, @oldest_event_time,
|
73
|
+
@event_format, @blob_max_delay,
|
74
|
+
@log_state, @sub_state, @file_size
|
75
|
+
]
|
76
|
+
end
|
77
|
+
|
78
|
+
def tuple_to_context ( tuple )
|
79
|
+
( @start_time, @action, @instrumentation_key, @table_id,
|
80
|
+
@storage_account_name, @container_name, @blob_name,
|
81
|
+
@uploaded_block_ids, @uploaded_block_numbers,
|
82
|
+
@uploaded_events_count, @uploaded_bytesize, @oldest_event_time,
|
83
|
+
@event_format, @blob_max_delay,
|
84
|
+
@log_state, @sub_state, @file_size ) = tuple
|
85
|
+
end
|
86
|
+
|
87
|
+
def context_to_table_entity
|
88
|
+
{ :start_time => @start_time, :instrumentation_key => @instrumentation_key, :table_id => @table_id,
|
89
|
+
:storage_account_name => @storage_account_name, :container_name => @container_name, :blob_name => @blob_name,
|
90
|
+
:uploaded_block_ids => @uploaded_block_ids.to_s, :uploaded_block_numbers => @uploaded_block_numbers.to_s,
|
91
|
+
:uploaded_events_count => @uploaded_events_count, :uploaded_bytesize => @uploaded_bytesize, :oldest_event_time => @oldest_event_time,
|
92
|
+
:log_state => @log_state, :sub_state => @sub_state, :file_size => @file_size
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,113 @@
|
|
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
|
+
|
22
|
+
class LogStash::Outputs::Application_insights
|
23
|
+
class Local_file
|
24
|
+
|
25
|
+
attr_reader :bytesize
|
26
|
+
attr_reader :events_count
|
27
|
+
attr_reader :oldest_event_time
|
28
|
+
|
29
|
+
attr_reader :file_size
|
30
|
+
|
31
|
+
public
|
32
|
+
|
33
|
+
def initialize ( filename, is_gzip_file )
|
34
|
+
@file_name = filename
|
35
|
+
@writer = write_file = File.new( @file_name, File::RDWR|File::CREAT )
|
36
|
+
@writer = Zlib::GzipWriter.new( write_file ) if is_gzip_file
|
37
|
+
@read_file = nil
|
38
|
+
@bytesize = 0
|
39
|
+
@events_count = 0
|
40
|
+
@first_block_number = nil
|
41
|
+
@next_block_number = nil
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def seal
|
46
|
+
if @writer
|
47
|
+
@writer.close
|
48
|
+
@writer = nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def close_read
|
54
|
+
@read_file.close if @read_file
|
55
|
+
@read_file = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def get_next_block
|
60
|
+
block = Block.new
|
61
|
+
block.done_time = @done_time
|
62
|
+
block.oldest_event_time = @oldest_event_time
|
63
|
+
|
64
|
+
unless @read_file
|
65
|
+
@read_file = File.new( @file_name, File::RDWR )
|
66
|
+
@file_size = @read_file.size
|
67
|
+
@blocks_num = ( @file_size + BLOB_BLOCK_MAX_BYTESIZE - 1 ) / BLOB_BLOCK_MAX_BYTESIZE
|
68
|
+
@events_per_block = @events_count / @blocks_num
|
69
|
+
block.events_count = @events_per_block + ( @events_count % @blocks_num )
|
70
|
+
@next_block_number = @first_block_number ||= Block.generate_block_number
|
71
|
+
block.block_numbers = [ @first_block_number ]
|
72
|
+
else
|
73
|
+
block.block_numbers = [ @next_block_number ]
|
74
|
+
block.events_count = @events_per_block
|
75
|
+
end
|
76
|
+
@next_block_number += 1
|
77
|
+
block.bytes = @read_file.read(BLOB_BLOCK_MAX_BYTESIZE)
|
78
|
+
return nil if block.bytes.nil? || 0 == block.bytes.length
|
79
|
+
block.bytesize = block.bytes.length
|
80
|
+
State.instance.inc_upload_bytesize( block.bytesize )
|
81
|
+
block
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def << ( block )
|
86
|
+
@bytesize += block.bytesize
|
87
|
+
@events_count += block.events_count
|
88
|
+
|
89
|
+
@writer.write( block.bytes )
|
90
|
+
State.instance.dec_upload_bytesize( block.bytesize )
|
91
|
+
|
92
|
+
@oldest_event_time = block.oldest_event_time if @oldest_event_time.nil? || block.oldest_event_time < @oldest_event_time
|
93
|
+
@done_time = block.done_time if @done_time.nil? || block.done_time > @done_time
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def dispose
|
98
|
+
@bytesize = nil
|
99
|
+
@events_count = nil
|
100
|
+
@done_time = nil
|
101
|
+
@oldest_event_time = nil
|
102
|
+
seal
|
103
|
+
close_read
|
104
|
+
File.delete( @file_name ) if @file_name
|
105
|
+
@file_name = nil
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,116 @@
|
|
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 Notification < Blob
|
23
|
+
|
24
|
+
def initialize ( tuple )
|
25
|
+
# super first parameter must be nil. blob first parameter is channel, otherwise it will pass storage_account_name as channel
|
26
|
+
super( tuple )
|
27
|
+
end
|
28
|
+
|
29
|
+
# must return whether notification was successful or failed
|
30
|
+
def notify
|
31
|
+
@action = :notify
|
32
|
+
@recoverable = [ :notify_failed_blob_not_accessible, :io_failure, :service_unavailable, :notification_process_down ]
|
33
|
+
@force_client = true # to enable get a client even if all storage_accounts marked dead
|
34
|
+
@info = "#{@action} #{@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}"
|
35
|
+
success = storage_io_block { submit }
|
36
|
+
if success
|
37
|
+
Telemetry.instance.track_event { { :name => "notified", :properties => context_to_table_entity } }
|
38
|
+
state_table_update
|
39
|
+
else
|
40
|
+
notify_retry_later
|
41
|
+
end
|
42
|
+
success
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def submit
|
48
|
+
set_blob_sas_url
|
49
|
+
@logger.debug { "blob_sas_url: #{@blob_sas_url}" }
|
50
|
+
payload = create_payload
|
51
|
+
@logger.debug { "notification payload: #{payload}" }
|
52
|
+
|
53
|
+
# assume that exceptions can be raised due to this method:
|
54
|
+
post_notification( @client.notifyClient, payload ) unless @configuration[:disable_notification]
|
55
|
+
|
56
|
+
@log_state = :notified
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def set_blob_sas_url
|
61
|
+
blob_url ="https://#{@storage_account_name}.blob.#{@configuration[:azure_storage_host_suffix]}/#{@container_name}/#{@blob_name}"
|
62
|
+
options_and_constrains = {:permissions => "r", :resource => "b", :expiry => ( Time.now.utc + @configuration[:blob_access_expiry_time] ).iso8601 }
|
63
|
+
@blob_sas_url = @client.storage_auth_sas.signed_uri( URI( blob_url ), options_and_constrains )
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def create_payload
|
68
|
+
notification_hash = {
|
69
|
+
:data => {
|
70
|
+
:baseType => DATA_BASE_TYPE,
|
71
|
+
:baseData => {
|
72
|
+
:ver => BASE_DATA_REQUIRED_VERSION,
|
73
|
+
:blobSasUri => @blob_sas_url.to_s,
|
74
|
+
:sourceName => @table_id,
|
75
|
+
:sourceVersion => @configuration[:notification_version].to_s
|
76
|
+
}
|
77
|
+
},
|
78
|
+
:ver => @configuration[:notification_version],
|
79
|
+
:name => REQUEST_NAME,
|
80
|
+
:time => Time.now.utc.iso8601,
|
81
|
+
:iKey => @instrumentation_key
|
82
|
+
}
|
83
|
+
notification_hash.to_json
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def post_notification ( http_client, body )
|
88
|
+
request = Azure::Core::Http::HttpRequest.new( :post, @configuration[:application_insights_endpoint], { :body => body, :client => http_client } )
|
89
|
+
request.headers['Content-Type'] = 'application/json; charset=utf-8'
|
90
|
+
request.headers['Accept'] = 'application/json'
|
91
|
+
@logger.debug { "send notification : \n endpoint: #{@configuration[:application_insights_endpoint]}\n body : #{body}" }
|
92
|
+
response = request.call
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def notify_retry_later
|
97
|
+
if :notify_failed_blob_not_accessible == @recovery
|
98
|
+
@sub_state = @recovery
|
99
|
+
@storage_recovery.recover_later( context_to_tuple, :notify, @storage_account_name )
|
100
|
+
|
101
|
+
elsif :invalid_instrumentation_key == @recovery || :invalid_table_id == @recovery
|
102
|
+
@sub_state = @recovery
|
103
|
+
Channels.instance.channel( @instrumentation_key, @table_id ).recover_later_notification( context_to_tuple )
|
104
|
+
|
105
|
+
else
|
106
|
+
if :notify_failed_blob_not_accessible == @sub_state
|
107
|
+
@storage_recovery.recover_later( context_to_tuple, :notify, @storage_account_name )
|
108
|
+
elsif :invalid_instrumentation_key == @sub_state || :invalid_table_id == @sub_state
|
109
|
+
Channels.instance.channel( @instrumentation_key, @table_id ).recover_later_notification( context_to_tuple )
|
110
|
+
else
|
111
|
+
@notification_recovery.recover_later( context_to_tuple )
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -66,19 +66,18 @@ class LogStash::Outputs::Application_insights
|
|
66
66
|
|
67
67
|
def recovery_thread
|
68
68
|
Thread.new do
|
69
|
-
blob = Blob.new
|
70
69
|
counter = Concurrent::AtomicFixnum.new(0)
|
71
70
|
|
72
71
|
loop do
|
73
72
|
tuple = @queue.pop
|
74
|
-
Stud.stoppable_sleep(Float::INFINITY, 1) { ( state_on?
|
73
|
+
Stud.stoppable_sleep(Float::INFINITY, 1) { ( state_on? || stopped? ) && 10 > counter.value }
|
75
74
|
|
76
|
-
if stopped? && !state_on?
|
75
|
+
if stopped? && !state_on?
|
77
76
|
recover_later( tuple )
|
78
77
|
else
|
79
78
|
counter.increment
|
80
79
|
Thread.new( counter, tuple ) do |counter, tuple|
|
81
|
-
|
80
|
+
Notification.new( tuple ).notify
|
82
81
|
counter.decrement
|
83
82
|
end
|
84
83
|
end
|
@@ -87,9 +86,9 @@ class LogStash::Outputs::Application_insights
|
|
87
86
|
end
|
88
87
|
end
|
89
88
|
|
90
|
-
def state_on?
|
89
|
+
def state_on?
|
91
90
|
return @notification_state_on if @notification_state_on
|
92
|
-
@notification_state_on = @test_notification.
|
91
|
+
@notification_state_on = @test_notification.test
|
93
92
|
return @notification_state_on if @notification_state_on
|
94
93
|
sleep( @resurrect_delay )
|
95
94
|
@notification_state_on
|
@@ -27,7 +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
|
-
@partition_key_prefix =configuration[:
|
30
|
+
@partition_key_prefix =configuration[:partition_key_prefix]
|
31
31
|
|
32
32
|
@closing = nil
|
33
33
|
@threads = []
|
@@ -85,7 +85,8 @@ class LogStash::Outputs::Application_insights
|
|
85
85
|
entities.each do |entity|
|
86
86
|
typed_tuple = nil
|
87
87
|
until typed_tuple || stopped?
|
88
|
-
|
88
|
+
blob.table_entity_to_context( entity.properties )
|
89
|
+
typed_tuple = blob.update_commited_or_uncommited_list
|
89
90
|
Stud.stoppable_sleep(60, 1) { stopped? } unless typed_tuple
|
90
91
|
end
|
91
92
|
|
@@ -0,0 +1,108 @@
|
|
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 State_table < Blob
|
23
|
+
|
24
|
+
def initialize( tuple )
|
25
|
+
# super first parameter must be nil. blob first parameter is channel, otherwise it will pass storage_account_name as channel
|
26
|
+
super( nil )
|
27
|
+
tuple_to_state( tuple ) if tuple
|
28
|
+
end
|
29
|
+
|
30
|
+
# return true on success
|
31
|
+
def state_table_insert
|
32
|
+
@action = :state_table_insert
|
33
|
+
@recoverable = [ :invalid_storage_key, :io_failure, :service_unavailable, :table_exist, :create_table, :table_busy, :entity_exist ]
|
34
|
+
@info = "#{@action} #{@log_state} #{@storage_account_name}/#{@container_name}/#{@blob_name}"
|
35
|
+
success = storage_io_block {
|
36
|
+
create_table_exist_recovery
|
37
|
+
if :entity_exist == @recovery
|
38
|
+
raise NotRecoverableError if :uploading == @log_state
|
39
|
+
else
|
40
|
+
entity_values = state_to_table_entity
|
41
|
+
entity_values[:PartitionKey] = "#{@configuration[:partition_key_prefix]}-#{@log_state}"
|
42
|
+
entity_values[:RowKey] = @blob_name.gsub("/","_")
|
43
|
+
@client.tableClient.insert_entity( @configuration[:state_table_name], entity_values )
|
44
|
+
end
|
45
|
+
}
|
46
|
+
@storage_recovery.recover_later( state_to_tuple, :state_table_update, @storage_account_name ) unless success || :uploading == @log_state
|
47
|
+
success
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def state_table_update
|
52
|
+
if :uploading == @log_state
|
53
|
+
state_table_delete
|
54
|
+
elsif :committed == @log_state
|
55
|
+
if state_table_insert && state_table_delete( :uploading )
|
56
|
+
State.instance.dec_pending_commits
|
57
|
+
State.instance.inc_pending_notifications
|
58
|
+
# this is not a recovery, it is actually enqueue to notify
|
59
|
+
@notification_recovery.enqueue( state_to_tuple )
|
60
|
+
end
|
61
|
+
elsif :notified == @log_state
|
62
|
+
if (!@configuration[:save_notified_blobs_records] || state_table_insert) && state_table_delete( :committed )
|
63
|
+
State.instance.dec_pending_notifications
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
# return tru on success
|
70
|
+
def state_table_delete ( state = nil )
|
71
|
+
state ||= @log_state
|
72
|
+
@action = :state_table_delete
|
73
|
+
@recoverable = [ :invalid_storage_key, :io_failure, :service_unavailable, :table_exist, :create_table, :table_busy, :create_resource ]
|
74
|
+
@info = "#{@action} #{state} #{@storage_account_name}/#{@container_name}/#{@blob_name}"
|
75
|
+
|
76
|
+
success = storage_io_block {
|
77
|
+
create_table_exist_recovery
|
78
|
+
if :create_resource == @recovery
|
79
|
+
@logger.info { "Note: delete entity failed, already deleted, #{@info}, state: #{state}, log_state: #{@log_state}" }
|
80
|
+
else
|
81
|
+
@client.tableClient.delete_entity( @configuration[:state_table_name], "#{@configuration[:partition_key_prefix]}-#{state}", @blob_name.gsub( "/", "_" ) )
|
82
|
+
end
|
83
|
+
}
|
84
|
+
@storage_recovery.recover_later( state_to_tuple, :state_table_update, @storage_account_name ) unless success
|
85
|
+
success
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# return entities
|
90
|
+
def state_table_query ( storage_account_name, filter , token )
|
91
|
+
@storage_account_name = storage_account_name
|
92
|
+
|
93
|
+
@action = :state_table_query
|
94
|
+
@recoverable = [ :invalid_storage_key, :io_failure, :service_unavailable, :table_exist, :create_table, :table_busy ]
|
95
|
+
@info = "#{@action} #{@storage_account_name}/#{@configuration[:state_table_name]}"
|
96
|
+
|
97
|
+
entities = nil
|
98
|
+
success = storage_io_block {
|
99
|
+
create_table_exist_recovery
|
100
|
+
options = { :filter => filter }
|
101
|
+
options[:continuation_token] = token if token
|
102
|
+
entities = @client.tableClient.query_entities( @configuration[:state_table_name], options )
|
103
|
+
}
|
104
|
+
entities
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|