zuora_connect 1.6.05 → 1.6.06
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/zuora_connect/api/v1/app_instance_controller.rb +16 -8
- data/app/models/zuora_connect/app_instance_base.rb +51 -93
- data/app/models/zuora_connect/login.rb +3 -3
- data/config/initializers/postgresql_adapter.rb +32 -0
- data/lib/metrics/influx/point_value.rb +79 -0
- data/lib/metrics/net.rb +218 -0
- data/lib/middleware/metrics_middleware.rb +36 -40
- data/lib/zuora_connect/configuration.rb +14 -6
- data/lib/zuora_connect/controllers/helpers.rb +1 -1
- data/lib/zuora_connect/version.rb +1 -1
- data/lib/zuora_connect.rb +2 -1
- metadata +9 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3aab6b6790b92249ea03f26bf79222ec77720566
|
4
|
+
data.tar.gz: 5f09e4567ab8a4bdac1defe735a5b43809295dd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b20a0da49a4ee3ff62950fb04419a5d3a98fe1d381ed9b9bcc04c7564c8e9d053db136740a82ff74ad950ce47e094aa63b66739dc35ba1a7d4924c95ab4541b
|
7
|
+
data.tar.gz: fe902be31ee9eb458cc92204cc4addb4ea50a973973247b6a1d44fb1b670a0d0dc35b1644ac5ff8248741b1926dd589e8d17372b1199975530fb98ddbc65a2b9
|
@@ -13,17 +13,25 @@ module ZuoraConnect
|
|
13
13
|
def drop
|
14
14
|
instance_id = @appinstance.id
|
15
15
|
if session["#{instance_id}::destroy"] && ZuoraConnect::AppInstance.where(:id => instance_id).size != 0
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
if @appinstance.drop_instance
|
17
|
+
ZuoraConnect::AppInstance.destroy(instance_id)
|
18
|
+
msg = Apartment::Tenant.drop(instance_id)
|
19
|
+
|
20
|
+
respond_to do |format|
|
21
|
+
if msg.error_message.present?
|
22
|
+
format.json {render json: {"message" => msg.error_message}, status: :bad_request }
|
23
|
+
else
|
24
|
+
format.json {render json: {}, status: :ok}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
else
|
28
|
+
respond_to do |format|
|
29
|
+
format.json {render json: {"message" => @appinstance.drop_message}, status: :bad_request}
|
30
|
+
end
|
23
31
|
end
|
24
32
|
else
|
25
33
|
respond_to do |format|
|
26
|
-
format.json {render :
|
34
|
+
format.json { render json: { "message" => "Unauthorized"}, status: :unauthorized }
|
27
35
|
end
|
28
36
|
end
|
29
37
|
end
|
@@ -4,8 +4,8 @@ module ZuoraConnect
|
|
4
4
|
default_scope {select(ZuoraConnect::AppInstance.column_names.delete_if {|x| ["catalog_mapping", "catalog"].include?(x) }) }
|
5
5
|
after_initialize :init
|
6
6
|
self.table_name = "zuora_connect_app_instances"
|
7
|
-
attr_accessor :options, :mode, :logins, :task_data, :last_refresh, :username, :password, :s3_client, :api_version
|
8
|
-
|
7
|
+
attr_accessor :options, :mode, :logins, :task_data, :last_refresh, :username, :password, :s3_client, :api_version, :drop_message
|
8
|
+
@@telegraf_host = nil
|
9
9
|
REFRESH_TIMEOUT = 2.minute #Used to determine how long to wait on current refresh call before executing another
|
10
10
|
INSTANCE_REFRESH_WINDOW = 30.minutes #Used to set how how long till app starts attempting to refresh cached task connect data
|
11
11
|
INSTANCE_REDIS_CACHE_PERIOD = 60.minutes #Used to determine how long to cached task data will live for
|
@@ -25,12 +25,31 @@ module ZuoraConnect
|
|
25
25
|
self.apartment_switch(nil, true)
|
26
26
|
end
|
27
27
|
|
28
|
+
def self.write_metric(series: 'Unknown', tags: {}, values: {})
|
29
|
+
begin
|
30
|
+
if @@telegraf_host.blank?
|
31
|
+
Rails.logger.debug('Need new connection') if ZuoraConnect.configuration.telegraf_debug
|
32
|
+
uri = URI.parse(ZuoraConnect.configuration.telegraf_endpoint)
|
33
|
+
@@telegraf_host = UDPSocket.new.tap do |socket|
|
34
|
+
socket.connect uri.host, uri.port
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@@telegraf_host.write InfluxDB::PointValue.new({series: series, tags: tags, values: values}).dump
|
38
|
+
rescue => ex
|
39
|
+
@@telegraf_host = nil
|
40
|
+
Rails.logger.tagged("Telegraf") do
|
41
|
+
logger.warn('Failed to write')
|
42
|
+
logger.warn(ex.class)
|
43
|
+
logger.warn(ex.message)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
28
47
|
|
29
48
|
# Methods for writing Telegraf metrics
|
30
49
|
|
31
50
|
# Returns the process type if any
|
32
51
|
def self.get_process_type
|
33
|
-
p_type =
|
52
|
+
p_type = 'Unknown'
|
34
53
|
if ENV['HOSTNAME'] && ENV['DEIS_APP']
|
35
54
|
temp = ENV['HOSTNAME'].split(ENV['DEIS_APP'])[1]
|
36
55
|
temp = temp.split(/(-[0-9a-zA-Z]{5})$/)[0] # remove the 5 char hash
|
@@ -40,57 +59,31 @@ module ZuoraConnect
|
|
40
59
|
end
|
41
60
|
|
42
61
|
# Write to telegraf
|
43
|
-
def self.write_to_telegraf(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
ZuoraConnect.configuration.
|
58
|
-
|
59
|
-
values: {response_time: response_time, db_time: db_runtime, view_time: view_runtime})
|
60
|
-
rescue => e
|
61
|
-
raise e
|
62
|
-
end
|
63
|
-
else
|
64
|
-
# non 200 requests
|
65
|
-
begin
|
66
|
-
ZuoraConnect.configuration.telegraf_client.write(ZuoraConnect.configuration.influxdb_series_name_inbound,
|
67
|
-
tags: {"app_name": "#{ZuoraConnect.configuration.app_name}", "controller": endpoint_name.split("#")[0] , "action": endpoint_name.split("#")[1], "content-type": content_type, method: method_name, status: status_code, process_type: p_type, "app_instance": app_instance},
|
68
|
-
values: {response_time: response_time})
|
69
|
-
rescue=> e
|
70
|
-
raise e
|
62
|
+
def self.write_to_telegraf(direction: nil, tags: {}, values: {})
|
63
|
+
Rails.logger.tagged("Telegraf") do
|
64
|
+
time = Benchmark.measure do |bench|
|
65
|
+
# To avoid writing metrics from rspec tests
|
66
|
+
if ENV['DEIS_APP'] || true
|
67
|
+
# Getting the process type
|
68
|
+
p_type = self.get_process_type
|
69
|
+
app_instance = Thread.current[:appinstance].present? ? Thread.current[:appinstance].id : 0
|
70
|
+
tags = tags.merge({ app_name: ZuoraConnect.configuration.app_name, process_type: p_type, app_instance: app_instance})
|
71
|
+
|
72
|
+
if direction == :inbound && ZuoraConnect.configuration.enable_inbound_metrics_flag
|
73
|
+
series = ZuoraConnect.configuration.influxdb_series_name_inbound
|
74
|
+
self.write_metric(series: series, tags: tags, values: values)
|
75
|
+
elsif direction == :outbound && ZuoraConnect.configuration.enable_outbound_metrics_flag
|
76
|
+
series = ZuoraConnect.configuration.influxdb_series_name_outbound
|
77
|
+
self.write_metric(series: series, tags: tags, values: values)
|
71
78
|
end
|
72
|
-
end
|
73
79
|
|
74
|
-
elsif direction == "outbound" && ZuoraConnect.configuration.enable_outbound_metrics_flag
|
75
|
-
# if there is an error
|
76
|
-
if error_type
|
77
|
-
begin
|
78
|
-
ZuoraConnect.configuration.telegraf_client.write(ZuoraConnect.configuration.influxdb_series_name_outbound,
|
79
|
-
tags: {"app_name": "#{ZuoraConnect.configuration.app_name}", endpoint: endpoint_name, status: status_code, process_type: p_type, "app_instance": app_instance, "function_name": function_name, method: method_name, "error_type": error_type},
|
80
|
-
values: {response_time: response_time})
|
81
|
-
rescue => e
|
82
|
-
raise e
|
83
|
-
end
|
84
|
-
else
|
85
|
-
begin
|
86
|
-
ZuoraConnect.configuration.telegraf_client.write(ZuoraConnect.configuration.influxdb_series_name_outbound,
|
87
|
-
tags: {"app_name": "#{ZuoraConnect.configuration.app_name}", endpoint: endpoint_name, status: status_code, process_type: p_type, "app_instance": app_instance, "function_name": function_name, method: method_name},
|
88
|
-
values: {response_time: response_time})
|
89
|
-
rescue => e
|
90
|
-
raise e
|
91
|
-
end
|
92
80
|
end
|
93
81
|
end
|
82
|
+
if ZuoraConnect.configuration.telegraf_debug
|
83
|
+
Rails.logger.debug(tags.to_s)
|
84
|
+
Rails.logger.debug(values.to_s)
|
85
|
+
Rails.logger.debug("Writing #{direction.capitalize}: #{time.real.round(4)}")
|
86
|
+
end
|
94
87
|
end
|
95
88
|
end
|
96
89
|
|
@@ -223,7 +216,6 @@ module ZuoraConnect
|
|
223
216
|
|
224
217
|
def refresh(session = nil)
|
225
218
|
refresh_count ||= 0
|
226
|
-
error_type = ""
|
227
219
|
start = Time.now
|
228
220
|
response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
|
229
221
|
response_time = Time.now - start
|
@@ -243,7 +235,6 @@ module ZuoraConnect
|
|
243
235
|
Rails.logger.info("[#{self.id}] REFRESH TASK - #{ex.class} Retrying(#{refresh_count})")
|
244
236
|
retry
|
245
237
|
else
|
246
|
-
error_type = "#{ex.class}"
|
247
238
|
Rails.logger.fatal("[#{self.id}] REFRESH TASK - #{ex.class} Failed #{refresh_count}x")
|
248
239
|
raise
|
249
240
|
end
|
@@ -255,16 +246,9 @@ module ZuoraConnect
|
|
255
246
|
end
|
256
247
|
retry
|
257
248
|
else
|
258
|
-
error_type = "#{ex.class}"
|
259
249
|
Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed #{refresh_count}x")
|
260
250
|
raise
|
261
251
|
end
|
262
|
-
ensure
|
263
|
-
# Writing to telegraf
|
264
|
-
status_code = response.code if response
|
265
|
-
endpoint_name = URI(ZuoraConnect.configuration.url).host
|
266
|
-
Thread.current[:appinstance].present? ? app_instance = Thread.current[:appinstance].id : app_instance = 0
|
267
|
-
ZuoraConnect::AppInstanceBase.write_to_telegraf("response_time": response_time, "status_code": status_code, "endpoint_name": endpoint_name, "direction": "outbound", "error_type": error_type, "function_name": "#{self.class}##{__method__}", "method_name": "GET", "app_instance": app_instance)
|
268
252
|
end
|
269
253
|
|
270
254
|
#### START Task Mathods ####
|
@@ -304,28 +288,13 @@ module ZuoraConnect
|
|
304
288
|
end
|
305
289
|
|
306
290
|
def updateOption(optionId, value)
|
307
|
-
|
308
|
-
start_time = Time.now
|
309
|
-
response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/application_options/#{optionId}/edit?value=#{value}",:body => {:access_token => self.username})
|
310
|
-
rescue => e
|
311
|
-
error_type = "#{e.class}"
|
312
|
-
ensure
|
313
|
-
end_time = Time.now
|
314
|
-
response_time = end_time - start_time
|
315
|
-
status_code = response.code if response
|
316
|
-
endpoint_name = URI(ZuoraConnect.configuration.url).host
|
317
|
-
Thread.current[:appinstance].present? ? app_instance = Thread.current[:appinstance].id : app_instance = 0
|
318
|
-
ZuoraConnect::AppInstanceBase.write_to_telegraf("response_time": response_time, "status_code": status_code, "endpoint_name": endpoint_name, "direction": "outbound", "error_type": error_type, "function_name": "#{self.class}##{__method__}", "method_name": "GET", "app_instance": app_instance)
|
319
|
-
return response
|
320
|
-
end
|
291
|
+
response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/application_options/#{optionId}/edit?value=#{value}",:body => {:access_token => self.username})
|
321
292
|
end
|
322
293
|
|
323
294
|
#This can update an existing login, add a new login, change to another existing login
|
324
295
|
#EXAMPLE: {"name": "ftp_login_14","username": "ftplogin7","tenant_type": "Custom","password": "test2","url": "www.ftp.com","custom_data": { "path": "/var/usr/test"}}
|
325
296
|
def update_logins(options)
|
326
297
|
update_login_count ||= 0
|
327
|
-
start_time = Time.now
|
328
|
-
error_type = ""
|
329
298
|
response = HTTParty.post(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}/logins",:body => {:access_token => self.username}.merge(options))
|
330
299
|
parsed_json = JSON.parse(response.body)
|
331
300
|
if response.code == 200
|
@@ -344,7 +313,6 @@ module ZuoraConnect
|
|
344
313
|
if (update_login_count += 1) < 3
|
345
314
|
retry
|
346
315
|
else
|
347
|
-
error_type = "#{ex.class}"
|
348
316
|
raise
|
349
317
|
end
|
350
318
|
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
|
@@ -354,16 +322,8 @@ module ZuoraConnect
|
|
354
322
|
end
|
355
323
|
retry
|
356
324
|
else
|
357
|
-
error_type = "#{ex.class}"
|
358
325
|
raise
|
359
326
|
end
|
360
|
-
ensure
|
361
|
-
end_time = Time.now
|
362
|
-
response_time = end_time - start_time
|
363
|
-
status_code = response.code if response
|
364
|
-
endpoint_name = URI(ZuoraConnect.configuration.url).host
|
365
|
-
Thread.current[:appinstance].present? ? app_instance = Thread.current[:appinstance].id : app_instance = 0
|
366
|
-
ZuoraConnect::AppInstanceBase.write_to_telegraf("response_time": response_time, "status_code": status_code, "endpoint_name": endpoint_name, "direction": "outbound", "error_type": error_type, "function_name": "#{self.class}##{__method__}", "method_name": "POST", "app_instance": app_instance)
|
367
327
|
end
|
368
328
|
#### END Task Mathods ####
|
369
329
|
|
@@ -382,7 +342,6 @@ module ZuoraConnect
|
|
382
342
|
|
383
343
|
def refresh_oauth
|
384
344
|
refresh_oauth_count ||= 0
|
385
|
-
error_type = ""
|
386
345
|
start = Time.now
|
387
346
|
params = {
|
388
347
|
:grant_type => "refresh_token",
|
@@ -409,7 +368,6 @@ module ZuoraConnect
|
|
409
368
|
Rails.logger.info("[#{self.id}] REFRESH OAUTH - #{ex.class} Retrying(#{refresh_oauth_count})")
|
410
369
|
retry
|
411
370
|
else
|
412
|
-
error_type = "#{ex.class}"
|
413
371
|
Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - #{ex.class} Failed #{refresh_oauth_count}x")
|
414
372
|
raise
|
415
373
|
end
|
@@ -424,15 +382,9 @@ module ZuoraConnect
|
|
424
382
|
Rails.logger.info("[#{self.id}] REFRESH OAUTH - Failed Retrying(#{refresh_oauth_count})")
|
425
383
|
retry
|
426
384
|
else
|
427
|
-
error_type = "#{ex.class}"
|
428
385
|
Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed #{refresh_oauth_count}x")
|
429
386
|
raise
|
430
387
|
end
|
431
|
-
ensure
|
432
|
-
status_code = response.code if response
|
433
|
-
endpoint_name = URI(ZuoraConnect.configuration.url).host
|
434
|
-
Thread.current[:appinstance].present? ? app_instance = Thread.current[:appinstance].id : app_instance = 0
|
435
|
-
ZuoraConnect::AppInstanceBase.write_to_telegraf("response_time": response_time, "status_code": status_code, "endpoint_name": endpoint_name, "direction": "outbound", "error_type": error_type, "function_name": "#{self.class}##{__method__}", "method_name": "POST", "app_instance": app_instance)
|
436
388
|
end
|
437
389
|
#### END Connect OAUTH methods ####
|
438
390
|
|
@@ -479,7 +431,7 @@ module ZuoraConnect
|
|
479
431
|
if defined?(Redis.current)
|
480
432
|
#Task data must be present and the last refresh cannot be old. We dont want to overwite new cache data with old
|
481
433
|
if self.task_data.present? && (self.last_refresh.to_i > INSTANCE_REFRESH_WINDOW.ago.to_i)
|
482
|
-
Rails.logger.
|
434
|
+
Rails.logger.debug("[#{self.id}] Caching AppInstance")
|
483
435
|
Redis.current.setex("AppInstance:#{self.id}", INSTANCE_REDIS_CACHE_PERIOD.to_i, encrypt_data(data: self.save_data))
|
484
436
|
end
|
485
437
|
Redis.current.del("Deleted:#{self.id}")
|
@@ -846,6 +798,12 @@ module ZuoraConnect
|
|
846
798
|
return true
|
847
799
|
end
|
848
800
|
|
801
|
+
#Method for overiding droping of an app instance
|
802
|
+
def drop_instance
|
803
|
+
self.drop_message = 'Ok to drop'
|
804
|
+
return true
|
805
|
+
end
|
806
|
+
|
849
807
|
def reload_attributes(selected_attributes)
|
850
808
|
raise "Attibutes must be array" if selected_attributes.class != Array
|
851
809
|
value_attributes = self.class.unscoped.where(:id=>id).select(selected_attributes).first.attributes
|
@@ -4,15 +4,15 @@ module ZuoraConnect
|
|
4
4
|
def initialize (fields)
|
5
5
|
@clients = {}
|
6
6
|
if fields["tenant_type"] == "Zuora" && fields["entities"] && fields["entities"].size > 0
|
7
|
-
@clients["Default"] = ::ZuoraAPI::
|
7
|
+
@clients["Default"] = ::ZuoraAPI::Basic.new(fields.map{|k,v| [k.to_sym, v]}.to_h)
|
8
8
|
@default_entity = fields["entities"][0]["id"] if fields["entities"].size == 1
|
9
9
|
fields["entities"].each do |entity|
|
10
10
|
login_fields = fields.map{|k,v| [k.to_sym, v]}.to_h
|
11
11
|
login_fields[:entity_id] = entity["id"]
|
12
|
-
@clients[entity["id"]] = ::ZuoraAPI::
|
12
|
+
@clients[entity["id"]] = ::ZuoraAPI::Basic.new(login_fields)
|
13
13
|
end
|
14
14
|
elsif fields["tenant_type"] == "Zuora"
|
15
|
-
@clients["Default"] = ::ZuoraAPI::
|
15
|
+
@clients["Default"] = ::ZuoraAPI::Basic.new(fields.map{|k,v| [k.to_sym, v]}.to_h)
|
16
16
|
end
|
17
17
|
|
18
18
|
self.attr_builder("available_entities",@clients.keys) if fields["tenant_type"] == "Zuora"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class PostgreSQLAdapter < AbstractAdapter
|
4
|
+
private
|
5
|
+
def load_additional_types(type_map, oids = nil)
|
6
|
+
initializer = OID::TypeMapInitializer.new(type_map)
|
7
|
+
if supports_ranges?
|
8
|
+
query = <<-SQL
|
9
|
+
SELECT DISTINCT on (t.typname) t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
10
|
+
FROM pg_type as t
|
11
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
12
|
+
SQL
|
13
|
+
else
|
14
|
+
query = <<-SQL
|
15
|
+
SELECT DISTINCT on (t.typname) t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
16
|
+
FROM pg_type as t
|
17
|
+
SQL
|
18
|
+
end
|
19
|
+
|
20
|
+
if oids
|
21
|
+
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
22
|
+
else
|
23
|
+
query += initializer.query_conditions_for_initial_load(type_map)
|
24
|
+
end
|
25
|
+
|
26
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
27
|
+
initializer.run(records)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module InfluxDB
|
2
|
+
# Convert data point to string using Line protocol
|
3
|
+
class PointValue
|
4
|
+
attr_reader :series, :values, :tags, :timestamp
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
@series = escape data[:series], :measurement
|
8
|
+
@values = escape_values data[:values]
|
9
|
+
@tags = escape_tags data[:tags]
|
10
|
+
@timestamp = data[:timestamp]
|
11
|
+
end
|
12
|
+
|
13
|
+
def dump
|
14
|
+
dump = @series.dup
|
15
|
+
dump << ",#{@tags}" if @tags
|
16
|
+
dump << " #{@values}"
|
17
|
+
dump << " #{@timestamp}" if @timestamp
|
18
|
+
dump
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
ESCAPES = {
|
24
|
+
measurement: [' '.freeze, ','.freeze],
|
25
|
+
tag_key: ['='.freeze, ' '.freeze, ','.freeze],
|
26
|
+
tag_value: ['='.freeze, ' '.freeze, ','.freeze],
|
27
|
+
field_key: ['='.freeze, ' '.freeze, ','.freeze, '"'.freeze],
|
28
|
+
field_value: ["\\".freeze, '"'.freeze],
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
private_constant :ESCAPES
|
32
|
+
|
33
|
+
def escape(str, type)
|
34
|
+
# rubocop:disable Layout/AlignParameters
|
35
|
+
str = str.encode "UTF-8".freeze, "UTF-8".freeze,
|
36
|
+
invalid: :replace,
|
37
|
+
undef: :replace,
|
38
|
+
replace: "".freeze
|
39
|
+
# rubocop:enable Layout/AlignParameters
|
40
|
+
|
41
|
+
ESCAPES[type].each do |ch|
|
42
|
+
str = str.gsub(ch) { "\\#{ch}" }
|
43
|
+
end
|
44
|
+
str
|
45
|
+
end
|
46
|
+
|
47
|
+
def escape_values(values)
|
48
|
+
return if values.nil?
|
49
|
+
values.map do |k, v|
|
50
|
+
key = escape(k.to_s, :field_key)
|
51
|
+
val = escape_value(v)
|
52
|
+
"#{key}=#{val}"
|
53
|
+
end.join(",".freeze)
|
54
|
+
end
|
55
|
+
|
56
|
+
def escape_value(value)
|
57
|
+
if value.is_a?(String)
|
58
|
+
'"'.freeze + escape(value, :field_value) + '"'.freeze
|
59
|
+
elsif value.is_a?(Integer)
|
60
|
+
"#{value}i"
|
61
|
+
else
|
62
|
+
value.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def escape_tags(tags)
|
67
|
+
return if tags.nil?
|
68
|
+
|
69
|
+
tags = tags.map do |k, v|
|
70
|
+
key = escape(k.to_s, :tag_key)
|
71
|
+
val = escape(v.to_s, :tag_value)
|
72
|
+
|
73
|
+
"#{key}=#{val}" unless key == "".freeze || val == "".freeze
|
74
|
+
end.compact
|
75
|
+
|
76
|
+
tags.join(",") unless tags.empty?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/metrics/net.rb
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
# Usage:
|
6
|
+
#
|
7
|
+
# require 'http_logger'
|
8
|
+
#
|
9
|
+
# == Setup logger
|
10
|
+
#
|
11
|
+
# HttpLogger.logger = Logger.new('/tmp/all.log')
|
12
|
+
# HttpLogger.log_headers = true
|
13
|
+
#
|
14
|
+
# == Do request
|
15
|
+
#
|
16
|
+
# res = Net::HTTP.start(url.host, url.port) { |http|
|
17
|
+
# http.request(req)
|
18
|
+
# }
|
19
|
+
# ...
|
20
|
+
#
|
21
|
+
# == View the log
|
22
|
+
#
|
23
|
+
# cat /tmp/all.log
|
24
|
+
class HttpLogger
|
25
|
+
class << self
|
26
|
+
attr_accessor :collapse_body_limit
|
27
|
+
attr_accessor :log_headers
|
28
|
+
attr_accessor :log_request_body
|
29
|
+
attr_accessor :log_response_body
|
30
|
+
attr_accessor :logger
|
31
|
+
attr_accessor :colorize
|
32
|
+
attr_accessor :ignore
|
33
|
+
attr_accessor :level
|
34
|
+
end
|
35
|
+
|
36
|
+
self.log_headers = false
|
37
|
+
self.log_request_body = true
|
38
|
+
self.log_response_body = true
|
39
|
+
self.colorize = true
|
40
|
+
self.collapse_body_limit = 5000
|
41
|
+
self.ignore = []
|
42
|
+
self.level = :debug
|
43
|
+
|
44
|
+
def self.perform(*args, &block)
|
45
|
+
instance.perform(*args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.instance
|
49
|
+
@instance ||= HttpLogger.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.deprecate_config(option)
|
53
|
+
warn "Net::HTTP.#{option} is deprecated. Use HttpLogger.#{option} instead."
|
54
|
+
end
|
55
|
+
|
56
|
+
def perform(http, request, request_body)
|
57
|
+
start_time = Time.now
|
58
|
+
tags = {}
|
59
|
+
response = yield
|
60
|
+
rescue => ex
|
61
|
+
tags = tags.merge({error_type: ex.class})
|
62
|
+
raise
|
63
|
+
ensure
|
64
|
+
values = {response_time: ((Time.now - start_time)*1000).round(2)}
|
65
|
+
if require_logging?(http, request)
|
66
|
+
tags = tags.merge({endpoint: http.address, method: request.method})
|
67
|
+
log_request_url(http, request, start_time)
|
68
|
+
log_request_body(request)
|
69
|
+
log_request_headers(request)
|
70
|
+
if defined?(response) && response
|
71
|
+
tags = tags.merge({status: response.code.to_i})
|
72
|
+
log_response_code(response)
|
73
|
+
log_response_headers(response)
|
74
|
+
log_response_body(response.body)
|
75
|
+
end
|
76
|
+
ZuoraConnect::AppInstance.write_to_telegraf(direction: :outbound, tags: tags, values: values)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
def log_request_url(http, request, start_time)
|
83
|
+
ofset = Time.now - start_time
|
84
|
+
log("HTTP #{request.method} (%0.2fms)" % (ofset * 1000), request_url(http, request))
|
85
|
+
end
|
86
|
+
|
87
|
+
def request_url(http, request)
|
88
|
+
URI.decode("http#{"s" if http.use_ssl?}://#{http.address}:#{http.port}#{request.path}")
|
89
|
+
end
|
90
|
+
|
91
|
+
def log_request_headers(request)
|
92
|
+
if self.class.log_headers
|
93
|
+
request.each_capitalized { |k,v| log("HTTP request header", "#{k}: #{v}") }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
HTTP_METHODS_WITH_BODY = Set.new(%w(POST PUT GET PATCH))
|
98
|
+
|
99
|
+
def log_request_body(request)
|
100
|
+
if self.class.log_request_body
|
101
|
+
if HTTP_METHODS_WITH_BODY.include?(request.method)
|
102
|
+
if (body = request.body) && !body.empty?
|
103
|
+
log("Request body", truncate_body(body))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def log_response_code(response)
|
110
|
+
log("Response status", "#{response.class} (#{response.code})")
|
111
|
+
end
|
112
|
+
|
113
|
+
def log_response_headers(response)
|
114
|
+
if self.class.log_headers
|
115
|
+
response.each_capitalized { |k,v| log("HTTP response header", "#{k}: #{v}") }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def log_response_body(body)
|
120
|
+
if self.class.log_response_body
|
121
|
+
if body.is_a?(Net::ReadAdapter)
|
122
|
+
log("Response body", "<impossible to log>")
|
123
|
+
else
|
124
|
+
if body && !body.empty?
|
125
|
+
log("Response body", truncate_body(body))
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def require_logging?(http, request)
|
132
|
+
self.logger && !ignored?(http, request) && (http.started? || fakeweb?(http, request))
|
133
|
+
end
|
134
|
+
|
135
|
+
def ignored?(http, request)
|
136
|
+
url = request_url(http, request)
|
137
|
+
self.class.ignore.any? do |pattern|
|
138
|
+
url =~ pattern
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def fakeweb?(http, request)
|
143
|
+
return false unless defined?(::FakeWeb)
|
144
|
+
uri = ::FakeWeb::Utility.request_uri_as_string(http, request)
|
145
|
+
method = request.method.downcase.to_sym
|
146
|
+
::FakeWeb.registered_uri?(method, uri)
|
147
|
+
end
|
148
|
+
|
149
|
+
def truncate_body(body)
|
150
|
+
if collapse_body_limit && collapse_body_limit > 0 && body && body.size >= collapse_body_limit
|
151
|
+
body_piece_size = collapse_body_limit / 2
|
152
|
+
body[0..body_piece_size] +
|
153
|
+
"\n\n<some data truncated>\n\n" +
|
154
|
+
body[(body.size - body_piece_size)..body.size]
|
155
|
+
else
|
156
|
+
body
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def log(message, dump)
|
161
|
+
self.logger.send(self.class.level, format_log_entry(message, dump)) if Rails.env.to_sym == :development
|
162
|
+
end
|
163
|
+
|
164
|
+
def format_log_entry(message, dump = nil)
|
165
|
+
if self.class.colorize
|
166
|
+
message_color, dump_color = "4;32;1", "0;1"
|
167
|
+
log_entry = " \e[#{message_color}m#{message}\e[0m "
|
168
|
+
log_entry << "\e[#{dump_color}m%#{String === dump ? 's' : 'p'}\e[0m" % dump if dump
|
169
|
+
log_entry
|
170
|
+
else
|
171
|
+
"%s %s" % [message, dump]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def logger
|
176
|
+
self.class.logger
|
177
|
+
end
|
178
|
+
|
179
|
+
def collapse_body_limit
|
180
|
+
self.class.collapse_body_limit
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class Net::HTTP
|
185
|
+
|
186
|
+
def self.log_headers=(value)
|
187
|
+
HttpLogger.deprecate_config("log_headers")
|
188
|
+
HttpLogger.log_headers = value
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.colorize=(value)
|
192
|
+
HttpLogger.deprecate_config("colorize")
|
193
|
+
HttpLogger.colorize = value
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.logger=(value)
|
197
|
+
HttpLogger.deprecate_config("logger")
|
198
|
+
HttpLogger.logger = value
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
alias_method :request_without_logging, :request
|
203
|
+
|
204
|
+
def request(request, body = nil, &block)
|
205
|
+
HttpLogger.perform(self, request, body) do
|
206
|
+
request_without_logging(request, body, &block)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
if defined?(Rails)
|
212
|
+
if defined?(ActiveSupport) && ActiveSupport.respond_to?(:on_load)
|
213
|
+
# Rails3
|
214
|
+
ActiveSupport.on_load(:after_initialize) do
|
215
|
+
HttpLogger.logger = Rails.logger unless HttpLogger.logger
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -6,38 +6,37 @@ module Middleware
|
|
6
6
|
|
7
7
|
# This method is triggered when a non error page is loaded (not 404)
|
8
8
|
def call(name, started, finished, unique_id, payload)
|
9
|
-
|
10
9
|
if ZuoraConnect.configuration.enable_inbound_metrics_flag == true
|
11
|
-
|
12
10
|
# If the url contains any css or JavaScript files then do not collect metrics for them
|
13
|
-
|
14
|
-
if block_words.any? { |word| payload[:path].include?(word) }
|
15
|
-
return nil
|
16
|
-
end
|
11
|
+
return nil if ["css", "assets", "jpg", "png", "jpeg", "ico"].any? { |word| payload[:path].include?(word) }
|
17
12
|
|
18
13
|
# Getting the endpoint and the content_type
|
19
14
|
content_hash = {:html => "text/html", :js => "application/javascript", :json => "application/json"}
|
20
|
-
content_hash.key?(payload[:format]) ?
|
21
|
-
request_path = "#{payload[:controller]}##{payload[:action]}"
|
22
|
-
response_time = finished-started
|
15
|
+
content_type = content_hash.key?(payload[:format]) ? content_hash[payload[:format]] : payload[:format]
|
23
16
|
|
24
17
|
# payloads with 500 requests do not have status as it is not set by the controller
|
25
18
|
# https://github.com/rails/rails/issues/33335
|
26
|
-
status_code = payload[:status] ? payload[:status] : payload[:exception_object].present? ? 500 : ""
|
19
|
+
#status_code = payload[:status] ? payload[:status] : payload[:exception_object].present? ? 500 : ""
|
20
|
+
if payload[:exception].present?
|
21
|
+
status_code, exception = [500, payload[:exception].first]
|
22
|
+
else
|
23
|
+
status_code, exception = [payload[:status], nil]
|
24
|
+
end
|
27
25
|
|
28
|
-
|
29
|
-
ZuoraConnect::AppInstanceBase.write_to_telegraf("endpoint_name": request_path, "method_name": payload[:method], "status_code": status_code, "response_time": response_time, "db_runtime": payload[:db_runtime].to_f, "view_runtime": payload[:view_runtime], "content_type": content_type, "direction": "inbound")
|
26
|
+
tags = {method: payload[:method], status: status_code, error_type: exception, content_type: content_type, controller: payload[:controller], action: payload[:action]}.compact
|
30
27
|
|
28
|
+
values = {view_time: payload[:view_runtime], db_time: payload[:db_runtime], response_time: ((finished-started)*1000)}.compact
|
29
|
+
values = values.map{ |k,v| [k,v.round(2)]}.to_h
|
30
|
+
|
31
|
+
ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: :inbound, tags: tags, values: values)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
|
-
|
36
36
|
class MetricsMiddleware
|
37
37
|
|
38
38
|
require "zuora_connect/version"
|
39
39
|
require "zuora_api/version"
|
40
|
-
require "telegraf"
|
41
40
|
|
42
41
|
def initialize(app)
|
43
42
|
@app = app
|
@@ -46,15 +45,10 @@ module Middleware
|
|
46
45
|
def call(env)
|
47
46
|
start_time = Time.now
|
48
47
|
@status, @headers, @response = @app.call(env)
|
49
|
-
|
48
|
+
|
50
49
|
if ZuoraConnect.configuration.enable_inbound_metrics_flag == true
|
51
50
|
# If the url contains any CSS or JavaScript files then do not collect metrics for them
|
52
|
-
|
53
|
-
if block_words.any? { |word| env['PATH_INFO'].include?(word) }
|
54
|
-
return [@status, @headers, @response]
|
55
|
-
end
|
56
|
-
|
57
|
-
response_time = end_time - start_time
|
51
|
+
return [@status, @headers, @response] if ["css", "assets", "jpg", "png", "jpeg", "ico"].any? { |word| env['PATH_INFO'].include?(word) }
|
58
52
|
|
59
53
|
if defined? Prometheus
|
60
54
|
#Prometheus Stuff
|
@@ -86,28 +80,30 @@ module Middleware
|
|
86
80
|
|
87
81
|
end
|
88
82
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
#
|
97
|
-
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
83
|
+
|
84
|
+
# Uncomment following block of code for handling engine requests/requests without controller
|
85
|
+
# else
|
86
|
+
# # Handling requests which do not have controllers (engines)
|
87
|
+
if env["SCRIPT_NAME"].present?
|
88
|
+
controller_path = "#{env['SCRIPT_NAME'][1..-1]}"
|
89
|
+
controller_path = controller_path.sub("/", "::")
|
90
|
+
request_path = "#{controller_path}#UnknownAction"
|
91
|
+
else
|
92
|
+
# Writing to telegraf: Handle 404
|
93
|
+
if [404].include?(@status)
|
94
|
+
content_type = @headers['Content-Type'].split(';')[0] if @headers['Content-Type']
|
95
|
+
|
96
|
+
tags = {status: @status, content_type: content_type}
|
97
|
+
|
98
|
+
tags = tags.merge({controller: 'ActionController', action: 'RoutingError' })
|
99
|
+
|
100
|
+
values = {response_time: ((Time.now - start_time)*1000).round(2) }
|
101
|
+
|
102
|
+
ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: :inbound, tags: tags, values: values)
|
103
|
+
end
|
105
104
|
end
|
106
105
|
end
|
107
106
|
[@status, @headers, @response]
|
108
|
-
|
109
107
|
end
|
110
|
-
|
111
108
|
end
|
112
|
-
|
113
109
|
end
|
@@ -1,6 +1,13 @@
|
|
1
1
|
module ZuoraConnect
|
2
2
|
class Configuration
|
3
|
-
|
3
|
+
|
4
|
+
attr_accessor :default_locale, :default_time_zone, :url, :mode, :delayed_job,:private_key, :additional_apartment_models
|
5
|
+
|
6
|
+
attr_accessor :telegraf_endpoint, :telegraf_debug, :enable_inbound_metrics_flag, :enable_outbound_metrics_flag, :custom_prometheus_update_block, :app_name, :influxdb_series_name_inbound, :influxdb_series_name_outbound
|
7
|
+
|
8
|
+
attr_accessor :oauth_client_id, :oauth_client_secret, :oauth_client_redirect_uri
|
9
|
+
|
10
|
+
attr_accessor :dev_mode_logins, :dev_mode_options, :dev_mode_mode, :dev_mode_appinstance, :dev_mode_user, :dev_mode_pass, :dev_mode_admin, :dev_mode_secret_access_key,:dev_mode_access_key_id,:aws_region, :s3_bucket_name, :s3_folder_name
|
4
11
|
|
5
12
|
def initialize
|
6
13
|
@default_locale = :en
|
@@ -8,19 +15,20 @@ module ZuoraConnect
|
|
8
15
|
@url = "https://connect.zuora.com"
|
9
16
|
@mode = "Production"
|
10
17
|
@delayed_job = false
|
11
|
-
@use_s3 = false
|
12
18
|
@private_key = ENV["CONNECT_KEY"]
|
13
19
|
@additional_apartment_models = []
|
20
|
+
|
21
|
+
|
22
|
+
# Setting the app name for telegraf write
|
14
23
|
@telegraf_endpoint = 'udp://telegraf-app-metrics.monitoring.svc.cluster.local:8094'
|
24
|
+
@telegraf_debug = false
|
15
25
|
@enable_inbound_metrics_flag = false
|
16
26
|
@enable_outbound_metrics_flag = false
|
17
|
-
@telegraf_client = Telegraf::Agent.new @telegraf_endpoint
|
18
|
-
|
19
|
-
# Setting the app name for telegraf write
|
20
|
-
@app_name = ENV['DEIS_APP'].present? ? "#{ENV['DEIS_APP']}" : "#{Rails.application.class.parent_name}"
|
21
27
|
@influxdb_series_name_inbound = "request-inbound"
|
22
28
|
@influxdb_series_name_outbound = "request-outbound"
|
23
29
|
|
30
|
+
@app_name = ENV['DEIS_APP'].present? ? "#{ENV['DEIS_APP']}" : "#{Rails.application.class.parent_name}"
|
31
|
+
|
24
32
|
# OAuth Settings
|
25
33
|
@oauth_client_id = ""
|
26
34
|
@oauth_client_secret = ""
|
@@ -21,7 +21,7 @@ module ZuoraConnect
|
|
21
21
|
check_instance
|
22
22
|
end
|
23
23
|
end
|
24
|
-
Rails.logger.
|
24
|
+
Rails.logger.debug("[#{@appinstance.blank? ? "N/A" : @appinstance.id}] Authenticate App API Request Completed In - #{(Time.now - start_time).round(2)}s")
|
25
25
|
end
|
26
26
|
|
27
27
|
def authenticate_connect_app_request
|
data/lib/zuora_connect.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zuora_connect
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.06
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Connect Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: apartment
|
@@ -36,20 +36,20 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 1.
|
39
|
+
version: 1.6.0
|
40
40
|
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: 1.
|
42
|
+
version: 1.6.0
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: 1.
|
49
|
+
version: 1.6.0
|
50
50
|
- - ">="
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: 1.
|
52
|
+
version: 1.6.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: httparty
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,20 +106,6 @@ dependencies:
|
|
106
106
|
- - ">="
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: '0'
|
109
|
-
- !ruby/object:Gem::Dependency
|
110
|
-
name: telegraf
|
111
|
-
requirement: !ruby/object:Gem::Requirement
|
112
|
-
requirements:
|
113
|
-
- - "~>"
|
114
|
-
- !ruby/object:Gem::Version
|
115
|
-
version: 0.4.0
|
116
|
-
type: :runtime
|
117
|
-
prerelease: false
|
118
|
-
version_requirements: !ruby/object:Gem::Requirement
|
119
|
-
requirements:
|
120
|
-
- - "~>"
|
121
|
-
- !ruby/object:Gem::Version
|
122
|
-
version: 0.4.0
|
123
109
|
- !ruby/object:Gem::Dependency
|
124
110
|
name: railties
|
125
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -252,6 +238,7 @@ files:
|
|
252
238
|
- app/views/zuora_connect/static/session_error.html.erb
|
253
239
|
- config/initializers/apartment.rb
|
254
240
|
- config/initializers/object_method_hooks.rb
|
241
|
+
- config/initializers/postgresql_adapter.rb
|
255
242
|
- config/initializers/prometheus.rb
|
256
243
|
- config/initializers/redis.rb
|
257
244
|
- config/initializers/resque.rb
|
@@ -267,6 +254,8 @@ files:
|
|
267
254
|
- db/migrate/20110503003603_add_catalog_mappings_to_app_instance.rb
|
268
255
|
- db/migrate/20110503003604_catalog_default.rb
|
269
256
|
- db/migrate/20180301052853_add_catalog_attempted_at.rb
|
257
|
+
- lib/metrics/influx/point_value.rb
|
258
|
+
- lib/metrics/net.rb
|
270
259
|
- lib/middleware/metrics_middleware.rb
|
271
260
|
- lib/resque/additions.rb
|
272
261
|
- lib/resque/dynamic_queues.rb
|