Skip to content

Commit 003b85e

Browse files
authored
Retry failed requests inifinitely (#9)
Wait between retries in increasing steps. Fixes #8
2 parents 17a63c6 + 31d032b commit 003b85e

File tree

4 files changed

+31
-56
lines changed

4 files changed

+31
-56
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ input {
224224
225225
**Important:** The user which is sending the data on behalf of other tenants requires a specific role. This role is defined in the configuration file of the monasca-log-api as ```delegate_roles```.
226226
227+
#### Exception handling
228+
229+
This output plugin will sleep for maximum 60 seconds after the request to
230+
Monasca API fails and will retry the request until successful.
231+
227232
## Open tasks
228233
* Language translations (Replace hardcoded String messages with a configuration/language file)
229-
* Exception handling (monasca-api requests)
234+
* Unit tests for failed requests handling

lib/logstash/outputs/monasca_log_api.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def start_time_check
209209

210210
def stop_time_check
211211
#ensure that entries buffered in queue will be handled before stop
212+
@stopping = true
212213
@mutex.synchronize do
213214
send_logs
214215
end
@@ -226,6 +227,7 @@ def send_logs
226227
token = LogStash::Outputs::Keystone::Token.instance
227228
@logger.debug("Sending #{@logs[JSON_LOGS].size} logs")
228229
retry_tries = 5
230+
attempt = 0
229231
begin
230232
tries ||= retry_tries
231233
@monasca_log_api_client.send_logs(@logs, token.id, @cross_tenant)
@@ -241,10 +243,19 @@ def send_logs
241243
else
242244
@logger.error("Unauthorized: #{e}. Requesting new token failed "\
243245
"after #{retry_tries} retries.")
246+
247+
sleep_for_retry(60)
248+
retry
244249
end
245250
rescue => e
246-
@logger.error('Sending event to monasca-log-api threw exception',
251+
attempt += 1
252+
sleep_for = get_sleep_time(attempt)
253+
@logger.error("Sending event to monasca-log-api threw exception, "\
254+
"will sleep for #{sleep_for} seconds.",
247255
:exceptionew => e)
256+
257+
sleep_for_retry(sleep_for)
258+
retry
248259
end
249260
@logs.clear
250261
initialize_logs_object
@@ -270,4 +281,13 @@ def check_config
270281
raise LogStash::ConfigurationError, err
271282
end
272283
end
284+
285+
def get_sleep_time(attempt)
286+
sleep_for = attempt**2
287+
sleep_for <= 60 ? sleep_for : 60
288+
end
289+
290+
def sleep_for_retry(duration)
291+
Stud.stoppable_sleep(duration) { @stopping }
292+
end
273293
end

logstash-output-monasca_log_api.gemspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |s|
22
s.name = 'logstash-output-monasca_log_api'
3-
s.version = '1.0.3'
3+
s.version = '1.0.4'
44
s.licenses = ['Apache-2.0']
55
s.summary = 'This gem is a logstash output plugin to connect via http to monasca-log-api.'
66
s.description = 'This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program'
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
2222
s.add_runtime_dependency 'logstash-core', '~> 2.0'
2323
s.add_runtime_dependency 'logstash-codec-plain', '~> 2.0'
2424
s.add_runtime_dependency 'logstash-codec-json', '~> 2.0'
25+
s.add_runtime_dependency 'stud', '~> 0.0.22'
2526
s.add_development_dependency 'logstash-devutils', '~> 0.0.14'
2627
s.add_development_dependency 'simplecov', '~> 0.10'
2728
s.add_development_dependency 'simplecov-rcov', '~> 0.2.0'

spec/outputs/monasca_log_api_spec.rb

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -422,23 +422,6 @@
422422
end
423423
end
424424

425-
context 'when sending logs raise exception' do
426-
it 'logs a failure' do
427-
expect_any_instance_of(Cabin::Channel).to receive(:error)
428-
429-
monasca_log_api = LogStash::Outputs::MonascaLogApi.new(complete_config)
430-
expect_any_instance_of(LogStash::Outputs::Monasca::MonascaLogApiClient)
431-
.to receive(:send_logs)
432-
.and_raise(Errno::ETIMEDOUT)
433-
expect_any_instance_of(LogStash::Outputs::Keystone::KeystoneClient)
434-
.to receive(:authenticate).and_return(valid_token)
435-
436-
monasca_log_api.register
437-
monasca_log_api.multi_receive([event, event])
438-
expect { monasca_log_api.multi_receive([event]) }.to_not raise_error
439-
end
440-
end
441-
442425
context 'after sending logs' do
443426
it 'clears collected logs' do
444427
expect_any_instance_of(LogStash::Outputs::Monasca::MonascaLogApiClient)
@@ -480,42 +463,6 @@
480463
monasca_log_api.multi_receive([event, event, event])
481464
expect(LogStash::Outputs::Keystone::Token.instance.id).to eq(token_id)
482465
end
483-
484-
it 'if unauthorized, renew it' do
485-
stub_request(:post, monasca_log_api_url_post)
486-
.with(:headers =>
487-
{
488-
'Accept' => '*/*',
489-
'Content-Type' => 'application/json',
490-
'User-Agent' => 'Ruby',
491-
'X-Auth-Token' => 'f8cdafb7dce94444ad781a53ddaff693'
492-
})
493-
.to_return(:status => 401)
494-
495-
expect_any_instance_of(LogStash::Outputs::Monasca::MonascaLogApiClient)
496-
.to receive(:handle_response).exactly(5).times
497-
.and_raise(
498-
LogStash::Outputs::Monasca::MonascaLogApiClient::InvalidTokenError
499-
)
500-
501-
expect_any_instance_of(LogStash::Outputs::Keystone::KeystoneClient)
502-
.to receive(:authenticate).exactly(6).times
503-
.with(
504-
complete_config['username'],
505-
complete_config['user_domain_name'],
506-
complete_config['password'],
507-
complete_config['project_name'],
508-
complete_config['project_domain_name']
509-
)
510-
.and_return(expired_token, valid_token)
511-
512-
monasca_log_api = LogStash::Outputs::MonascaLogApi.new(complete_config)
513-
allow(monasca_log_api).to receive(:start_time_check)
514-
monasca_log_api.register
515-
expect(LogStash::Outputs::Keystone::Token.instance.id).to eq(old_token_id)
516-
monasca_log_api.multi_receive([event, event, event])
517-
expect(LogStash::Outputs::Keystone::Token.instance.id).to eq(token_id)
518-
end
519466
end
520467

521468
context 'when stopping plugin' do
@@ -528,4 +475,6 @@
528475
end
529476
end
530477

478+
# TODO add testing graceful recovery from exceptions
479+
531480
end

0 commit comments

Comments
 (0)