optimizely-sdk 3.3.1 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/optimizely.rb +199 -38
- data/lib/optimizely/audience.rb +2 -2
- data/lib/optimizely/config/datafile_project_config.rb +15 -3
- data/lib/optimizely/config/proxy_config.rb +34 -0
- data/lib/optimizely/config_manager/async_scheduler.rb +8 -4
- data/lib/optimizely/config_manager/http_project_config_manager.rb +50 -28
- data/lib/optimizely/config_manager/static_project_config_manager.rb +6 -2
- data/lib/optimizely/event/batch_event_processor.rb +67 -58
- data/lib/optimizely/event_dispatcher.rb +35 -17
- data/lib/optimizely/exceptions.rb +8 -9
- data/lib/optimizely/helpers/constants.rb +6 -3
- data/lib/optimizely/helpers/http_utils.rb +64 -0
- data/lib/optimizely/helpers/variable_type.rb +8 -1
- data/lib/optimizely/notification_center.rb +13 -5
- data/lib/optimizely/optimizely_config.rb +116 -0
- data/lib/optimizely/optimizely_factory.rb +54 -5
- data/lib/optimizely/version.rb +1 -1
- metadata +6 -18
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2019, Optimizely and contributors
|
4
|
+
# Copyright 2019-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -19,18 +19,21 @@ require_relative '../config/datafile_project_config'
|
|
19
19
|
require_relative '../error_handler'
|
20
20
|
require_relative '../exceptions'
|
21
21
|
require_relative '../helpers/constants'
|
22
|
+
require_relative '../helpers/http_utils'
|
22
23
|
require_relative '../logger'
|
23
24
|
require_relative '../notification_center'
|
24
25
|
require_relative '../project_config'
|
26
|
+
require_relative '../optimizely_config'
|
25
27
|
require_relative 'project_config_manager'
|
26
28
|
require_relative 'async_scheduler'
|
27
|
-
|
29
|
+
|
28
30
|
require 'json'
|
31
|
+
|
29
32
|
module Optimizely
|
30
33
|
class HTTPProjectConfigManager < ProjectConfigManager
|
31
34
|
# Config manager that polls for the datafile and updated ProjectConfig based on an update interval.
|
32
35
|
|
33
|
-
attr_reader :stopped
|
36
|
+
attr_reader :stopped, :optimizely_config
|
34
37
|
|
35
38
|
# Initialize config manager. One of sdk_key or url has to be set to be able to use.
|
36
39
|
#
|
@@ -48,6 +51,8 @@ module Optimizely
|
|
48
51
|
# error_handler - Provides a handle_error method to handle exceptions.
|
49
52
|
# skip_json_validation - Optional boolean param which allows skipping JSON schema
|
50
53
|
# validation upon object invocation. By default JSON schema validation will be performed.
|
54
|
+
# datafile_access_token - access token used to fetch private datafiles
|
55
|
+
# proxy_config - Optional proxy config instancea to configure making web requests through a proxy server.
|
51
56
|
def initialize(
|
52
57
|
sdk_key: nil,
|
53
58
|
url: nil,
|
@@ -60,24 +65,31 @@ module Optimizely
|
|
60
65
|
logger: nil,
|
61
66
|
error_handler: nil,
|
62
67
|
skip_json_validation: false,
|
63
|
-
notification_center: nil
|
68
|
+
notification_center: nil,
|
69
|
+
datafile_access_token: nil,
|
70
|
+
proxy_config: nil
|
64
71
|
)
|
65
72
|
@logger = logger || NoOpLogger.new
|
66
73
|
@error_handler = error_handler || NoOpErrorHandler.new
|
74
|
+
@access_token = datafile_access_token
|
67
75
|
@datafile_url = get_datafile_url(sdk_key, url, url_template)
|
68
76
|
@polling_interval = nil
|
69
77
|
polling_interval(polling_interval)
|
70
78
|
@blocking_timeout = nil
|
71
79
|
blocking_timeout(blocking_timeout)
|
72
80
|
@last_modified = nil
|
73
|
-
@async_scheduler = AsyncScheduler.new(method(:fetch_datafile_config), @polling_interval, auto_update, @logger)
|
74
|
-
@async_scheduler.start! if start_by_default == true
|
75
|
-
@stopped = false
|
76
81
|
@skip_json_validation = skip_json_validation
|
77
82
|
@notification_center = notification_center.is_a?(Optimizely::NotificationCenter) ? notification_center : NotificationCenter.new(@logger, @error_handler)
|
78
83
|
@config = datafile.nil? ? nil : DatafileProjectConfig.create(datafile, @logger, @error_handler, @skip_json_validation)
|
84
|
+
@optimizely_config = @config.nil? ? nil : OptimizelyConfig.new(@config).config
|
79
85
|
@mutex = Mutex.new
|
80
86
|
@resource = ConditionVariable.new
|
87
|
+
@async_scheduler = AsyncScheduler.new(method(:fetch_datafile_config), @polling_interval, auto_update, @logger)
|
88
|
+
# Start async scheduler in the end to avoid race condition where scheduler executes
|
89
|
+
# callback which makes use of variables not yet initialized by the main thread.
|
90
|
+
@async_scheduler.start! if start_by_default == true
|
91
|
+
@proxy_config = proxy_config
|
92
|
+
@stopped = false
|
81
93
|
end
|
82
94
|
|
83
95
|
def ready?
|
@@ -139,21 +151,20 @@ module Optimizely
|
|
139
151
|
end
|
140
152
|
|
141
153
|
def request_config
|
142
|
-
@logger.log(
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
headers = {
|
148
|
-
'Content-Type' => 'application/json'
|
149
|
-
}
|
154
|
+
@logger.log(Logger::DEBUG, "Fetching datafile from #{@datafile_url}")
|
155
|
+
headers = {}
|
156
|
+
headers['Content-Type'] = 'application/json'
|
157
|
+
headers['If-Modified-Since'] = @last_modified if @last_modified
|
158
|
+
headers['Authorization'] = "Bearer #{@access_token}" unless @access_token.nil?
|
150
159
|
|
151
|
-
|
160
|
+
# Cleaning headers before logging to avoid exposing authorization token
|
161
|
+
cleansed_headers = {}
|
162
|
+
headers.each { |key, value| cleansed_headers[key] = key == 'Authorization' ? '********' : value }
|
163
|
+
@logger.log(Logger::DEBUG, "Datafile request headers: #{cleansed_headers}")
|
152
164
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
timeout: Helpers::Constants::CONFIG_MANAGER['REQUEST_TIMEOUT']
|
165
|
+
begin
|
166
|
+
response = Helpers::HttpUtils.make_request(
|
167
|
+
@datafile_url, :get, nil, headers, Helpers::Constants::CONFIG_MANAGER['REQUEST_TIMEOUT'], @proxy_config
|
157
168
|
)
|
158
169
|
rescue StandardError => e
|
159
170
|
@logger.log(
|
@@ -163,6 +174,9 @@ module Optimizely
|
|
163
174
|
return nil
|
164
175
|
end
|
165
176
|
|
177
|
+
response_code = response.code.to_i
|
178
|
+
@logger.log(Logger::DEBUG, "Datafile response status code #{response_code}")
|
179
|
+
|
166
180
|
# Leave datafile and config unchanged if it has not been modified.
|
167
181
|
if response.code == '304'
|
168
182
|
@logger.log(
|
@@ -172,9 +186,14 @@ module Optimizely
|
|
172
186
|
return
|
173
187
|
end
|
174
188
|
|
175
|
-
|
176
|
-
|
177
|
-
|
189
|
+
if response_code >= 200 && response_code < 400
|
190
|
+
@logger.log(Logger::DEBUG, 'Successfully fetched datafile, generating Project config')
|
191
|
+
config = DatafileProjectConfig.create(response.body, @logger, @error_handler, @skip_json_validation)
|
192
|
+
@last_modified = response[Helpers::Constants::HTTP_HEADERS['LAST_MODIFIED']]
|
193
|
+
@logger.log(Logger::DEBUG, "Saved last modified header value from response: #{@last_modified}.")
|
194
|
+
else
|
195
|
+
@logger.log(Logger::DEBUG, "Datafile fetch failed, status: #{response.code}, message: #{response.message}")
|
196
|
+
end
|
178
197
|
|
179
198
|
config
|
180
199
|
end
|
@@ -190,6 +209,7 @@ module Optimizely
|
|
190
209
|
end
|
191
210
|
|
192
211
|
@config = config
|
212
|
+
@optimizely_config = OptimizelyConfig.new(config).config
|
193
213
|
|
194
214
|
@notification_center.send_notifications(NotificationCenter::NOTIFICATION_TYPES[:OPTIMIZELY_CONFIG_UPDATE])
|
195
215
|
|
@@ -279,17 +299,19 @@ module Optimizely
|
|
279
299
|
# SDK key to determine URL from which to fetch the datafile.
|
280
300
|
# Returns String representing URL to fetch datafile from.
|
281
301
|
if sdk_key.nil? && url.nil?
|
282
|
-
|
283
|
-
@
|
302
|
+
error_msg = 'Must provide at least one of sdk_key or url.'
|
303
|
+
@logger.log(Logger::ERROR, error_msg)
|
304
|
+
@error_handler.handle_error(InvalidInputsError.new(error_msg))
|
284
305
|
end
|
285
306
|
|
286
307
|
unless url
|
287
|
-
url_template ||= Helpers::Constants::CONFIG_MANAGER['DATAFILE_URL_TEMPLATE']
|
308
|
+
url_template ||= @access_token.nil? ? Helpers::Constants::CONFIG_MANAGER['DATAFILE_URL_TEMPLATE'] : Helpers::Constants::CONFIG_MANAGER['AUTHENTICATED_DATAFILE_URL_TEMPLATE']
|
288
309
|
begin
|
289
310
|
return (url_template % sdk_key)
|
290
311
|
rescue
|
291
|
-
|
292
|
-
@
|
312
|
+
error_msg = "Invalid url_template #{url_template} provided."
|
313
|
+
@logger.log(Logger::ERROR, error_msg)
|
314
|
+
@error_handler.handle_error(InvalidInputsError.new(error_msg))
|
293
315
|
end
|
294
316
|
end
|
295
317
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2019, Optimizely and contributors
|
4
|
+
# Copyright 2019-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -17,11 +17,13 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require_relative '../config/datafile_project_config'
|
20
|
+
require_relative '../optimizely_config'
|
20
21
|
require_relative 'project_config_manager'
|
22
|
+
|
21
23
|
module Optimizely
|
22
24
|
class StaticProjectConfigManager < ProjectConfigManager
|
23
25
|
# Implementation of ProjectConfigManager interface.
|
24
|
-
attr_reader :config
|
26
|
+
attr_reader :config, :optimizely_config
|
25
27
|
|
26
28
|
def initialize(datafile, logger, error_handler, skip_json_validation)
|
27
29
|
# Looks up and sets datafile and config based on response body.
|
@@ -38,6 +40,8 @@ module Optimizely
|
|
38
40
|
error_handler,
|
39
41
|
skip_json_validation
|
40
42
|
)
|
43
|
+
|
44
|
+
@optimizely_config = @config.nil? ? nil : OptimizelyConfig.new(@config).config
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
@@ -20,7 +20,7 @@ require_relative '../helpers/validator'
|
|
20
20
|
module Optimizely
|
21
21
|
class BatchEventProcessor < EventProcessor
|
22
22
|
# BatchEventProcessor is a batched implementation of the Interface EventProcessor.
|
23
|
-
# Events passed to the BatchEventProcessor are immediately added to
|
23
|
+
# Events passed to the BatchEventProcessor are immediately added to an EventQueue.
|
24
24
|
# The BatchEventProcessor maintains a single consumer thread that pulls events off of
|
25
25
|
# the BlockingQueue and buffers them for either a configured batch size or for a
|
26
26
|
# maximum duration before the resulting LogEvent is sent to the NotificationCenter.
|
@@ -30,13 +30,14 @@ module Optimizely
|
|
30
30
|
DEFAULT_BATCH_SIZE = 10
|
31
31
|
DEFAULT_BATCH_INTERVAL = 30_000 # interval in milliseconds
|
32
32
|
DEFAULT_QUEUE_CAPACITY = 1000
|
33
|
+
DEFAULT_TIMEOUT_INTERVAL = 5 # interval in seconds
|
33
34
|
|
34
35
|
FLUSH_SIGNAL = 'FLUSH_SIGNAL'
|
35
36
|
SHUTDOWN_SIGNAL = 'SHUTDOWN_SIGNAL'
|
36
37
|
|
37
38
|
def initialize(
|
38
39
|
event_queue: SizedQueue.new(DEFAULT_QUEUE_CAPACITY),
|
39
|
-
event_dispatcher:
|
40
|
+
event_dispatcher: nil,
|
40
41
|
batch_size: DEFAULT_BATCH_SIZE,
|
41
42
|
flush_interval: DEFAULT_BATCH_INTERVAL,
|
42
43
|
logger: NoOpLogger.new,
|
@@ -44,7 +45,7 @@ module Optimizely
|
|
44
45
|
)
|
45
46
|
@event_queue = event_queue
|
46
47
|
@logger = logger
|
47
|
-
@event_dispatcher = event_dispatcher
|
48
|
+
@event_dispatcher = event_dispatcher || EventDispatcher.new(logger: @logger)
|
48
49
|
@batch_size = if (batch_size.is_a? Integer) && positive_number?(batch_size)
|
49
50
|
batch_size
|
50
51
|
else
|
@@ -58,11 +59,9 @@ module Optimizely
|
|
58
59
|
DEFAULT_BATCH_INTERVAL
|
59
60
|
end
|
60
61
|
@notification_center = notification_center
|
61
|
-
@mutex = Mutex.new
|
62
|
-
@received = ConditionVariable.new
|
63
62
|
@current_batch = []
|
64
63
|
@started = false
|
65
|
-
|
64
|
+
@stopped = false
|
66
65
|
end
|
67
66
|
|
68
67
|
def start!
|
@@ -71,76 +70,61 @@ module Optimizely
|
|
71
70
|
return
|
72
71
|
end
|
73
72
|
@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval
|
74
|
-
@
|
73
|
+
@logger.log(Logger::INFO, 'Starting scheduler.')
|
74
|
+
if @wait_mutex.nil?
|
75
|
+
@wait_mutex = Mutex.new
|
76
|
+
@resource = ConditionVariable.new
|
77
|
+
end
|
78
|
+
@thread = Thread.new { run_queue }
|
75
79
|
@started = true
|
80
|
+
@stopped = false
|
76
81
|
end
|
77
82
|
|
78
83
|
def flush
|
79
|
-
@
|
80
|
-
|
81
|
-
@received.signal
|
82
|
-
end
|
84
|
+
@event_queue << FLUSH_SIGNAL
|
85
|
+
@wait_mutex.synchronize { @resource.signal }
|
83
86
|
end
|
84
87
|
|
85
88
|
def process(user_event)
|
86
89
|
@logger.log(Logger::DEBUG, "Received userEvent: #{user_event}")
|
87
90
|
|
88
|
-
if
|
91
|
+
# if the processor has been explicitly stopped. Don't accept tasks
|
92
|
+
if @stopped
|
89
93
|
@logger.log(Logger::WARN, 'Executor shutdown, not accepting tasks.')
|
90
94
|
return
|
91
95
|
end
|
92
96
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
97
|
+
# start if the processor hasn't been started
|
98
|
+
start! unless @started
|
99
|
+
|
100
|
+
begin
|
101
|
+
@event_queue.push(user_event, true)
|
102
|
+
@wait_mutex.synchronize { @resource.signal }
|
103
|
+
rescue => e
|
104
|
+
@logger.log(Logger::WARN, 'Payload not accepted by the queue: ' + e.message)
|
105
|
+
return
|
101
106
|
end
|
102
107
|
end
|
103
108
|
|
104
109
|
def stop!
|
105
110
|
return unless @started
|
106
111
|
|
107
|
-
@
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
+
@logger.log(Logger::INFO, 'Stopping scheduler.')
|
113
|
+
@event_queue << SHUTDOWN_SIGNAL
|
114
|
+
@wait_mutex.synchronize { @resource.signal }
|
115
|
+
@thread.join(DEFAULT_TIMEOUT_INTERVAL)
|
112
116
|
@started = false
|
113
|
-
@
|
114
|
-
@thread.exit
|
117
|
+
@stopped = true
|
115
118
|
end
|
116
119
|
|
117
120
|
private
|
118
121
|
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
@logger.log(
|
123
|
-
Logger::DEBUG,
|
124
|
-
'Deadline exceeded flushing current batch.'
|
125
|
-
)
|
126
|
-
flush_queue!
|
127
|
-
end
|
128
|
-
|
129
|
-
item = nil
|
130
|
-
|
131
|
-
@mutex.synchronize do
|
132
|
-
@received.wait(@mutex, 0.05)
|
133
|
-
item = @event_queue.pop if @event_queue.length.positive?
|
134
|
-
end
|
135
|
-
|
136
|
-
if item.nil?
|
137
|
-
sleep(0.05)
|
138
|
-
next
|
139
|
-
end
|
140
|
-
|
122
|
+
def process_queue
|
123
|
+
while @event_queue.length.positive?
|
124
|
+
item = @event_queue.pop
|
141
125
|
if item == SHUTDOWN_SIGNAL
|
142
|
-
@logger.log(Logger::
|
143
|
-
|
126
|
+
@logger.log(Logger::DEBUG, 'Received shutdown signal.')
|
127
|
+
return false
|
144
128
|
end
|
145
129
|
|
146
130
|
if item == FLUSH_SIGNAL
|
@@ -151,15 +135,35 @@ module Optimizely
|
|
151
135
|
|
152
136
|
add_to_batch(item) if item.is_a? Optimizely::UserEvent
|
153
137
|
end
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
def run_queue
|
142
|
+
loop do
|
143
|
+
if Helpers::DateTimeUtils.create_timestamp >= @flushing_interval_deadline
|
144
|
+
@logger.log(Logger::DEBUG, 'Deadline exceeded flushing current batch.')
|
145
|
+
|
146
|
+
break unless process_queue
|
147
|
+
|
148
|
+
flush_queue!
|
149
|
+
@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval
|
150
|
+
end
|
151
|
+
|
152
|
+
break unless process_queue
|
153
|
+
|
154
|
+
# what is the current interval to flush in seconds
|
155
|
+
interval = (@flushing_interval_deadline - Helpers::DateTimeUtils.create_timestamp) * 0.001
|
156
|
+
|
157
|
+
next unless interval.positive?
|
158
|
+
|
159
|
+
@wait_mutex.synchronize { @resource.wait(@wait_mutex, interval) }
|
160
|
+
end
|
154
161
|
rescue SignalException
|
155
|
-
@logger.log(Logger::
|
156
|
-
rescue
|
162
|
+
@logger.log(Logger::ERROR, 'Interrupted while processing buffer.')
|
163
|
+
rescue => e
|
157
164
|
@logger.log(Logger::ERROR, "Uncaught exception processing buffer. #{e.message}")
|
158
165
|
ensure
|
159
|
-
@logger.log(
|
160
|
-
Logger::INFO,
|
161
|
-
'Exiting processing loop. Attempting to flush pending events.'
|
162
|
-
)
|
166
|
+
@logger.log(Logger::INFO, 'Exiting processing loop. Attempting to flush pending events.')
|
163
167
|
flush_queue!
|
164
168
|
end
|
165
169
|
|
@@ -168,6 +172,11 @@ module Optimizely
|
|
168
172
|
|
169
173
|
log_event = Optimizely::EventFactory.create_log_event(@current_batch, @logger)
|
170
174
|
begin
|
175
|
+
@logger.log(
|
176
|
+
Logger::INFO,
|
177
|
+
'Flushing Queue.'
|
178
|
+
)
|
179
|
+
|
171
180
|
@event_dispatcher.dispatch_event(log_event)
|
172
181
|
@notification_center&.send_notifications(
|
173
182
|
NotificationCenter::NOTIFICATION_TYPES[:LOG_EVENT],
|
@@ -192,7 +201,7 @@ module Optimizely
|
|
192
201
|
@current_batch << user_event
|
193
202
|
return unless @current_batch.length >= @batch_size
|
194
203
|
|
195
|
-
@logger.log(Logger::DEBUG, 'Flushing on max batch size
|
204
|
+
@logger.log(Logger::DEBUG, 'Flushing on max batch size.')
|
196
205
|
flush_queue!
|
197
206
|
end
|
198
207
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2016-2017, Optimizely and contributors
|
4
|
+
# Copyright 2016-2017, 2019-2020 Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -15,7 +15,8 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
|
18
|
+
require_relative 'exceptions'
|
19
|
+
require_relative 'helpers/http_utils'
|
19
20
|
|
20
21
|
module Optimizely
|
21
22
|
class NoOpEventDispatcher
|
@@ -28,26 +29,43 @@ module Optimizely
|
|
28
29
|
# @api constants
|
29
30
|
REQUEST_TIMEOUT = 10
|
30
31
|
|
32
|
+
def initialize(logger: nil, error_handler: nil, proxy_config: nil)
|
33
|
+
@logger = logger || NoOpLogger.new
|
34
|
+
@error_handler = error_handler || NoOpErrorHandler.new
|
35
|
+
@proxy_config = proxy_config
|
36
|
+
end
|
37
|
+
|
31
38
|
# Dispatch the event being represented by the Event object.
|
32
39
|
#
|
33
40
|
# @param event - Event object
|
34
41
|
def dispatch_event(event)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
response = Helpers::HttpUtils.make_request(
|
43
|
+
event.url, event.http_verb, event.params.to_json, event.headers, REQUEST_TIMEOUT, @proxy_config
|
44
|
+
)
|
45
|
+
|
46
|
+
error_msg = "Event failed to dispatch with response code: #{response.code}"
|
47
|
+
|
48
|
+
case response.code.to_i
|
49
|
+
when 400...500
|
50
|
+
@logger.log(Logger::ERROR, error_msg)
|
51
|
+
@error_handler.handle_error(HTTPCallError.new("HTTP Client Error: #{response.code}"))
|
52
|
+
|
53
|
+
when 500...600
|
54
|
+
@logger.log(Logger::ERROR, error_msg)
|
55
|
+
@error_handler.handle_error(HTTPCallError.new("HTTP Server Error: #{response.code}"))
|
56
|
+
else
|
57
|
+
@logger.log(Logger::DEBUG, 'event successfully sent with response code ' + response.code.to_s)
|
50
58
|
end
|
59
|
+
rescue Timeout::Error => e
|
60
|
+
@logger.log(Logger::ERROR, "Request Timed out. Error: #{e}")
|
61
|
+
@error_handler.handle_error(e)
|
62
|
+
|
63
|
+
# Returning Timeout error to retain existing behavior.
|
64
|
+
e
|
65
|
+
rescue StandardError => e
|
66
|
+
@logger.log(Logger::ERROR, "Event failed to dispatch. Error: #{e}")
|
67
|
+
@error_handler.handle_error(e)
|
68
|
+
nil
|
51
69
|
end
|
52
70
|
end
|
53
71
|
end
|