zuora_connect 2.1.1 → 3.0.0.pre.a
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/static_controller.rb +84 -17
- data/app/models/zuora_connect/app_instance_base.rb +168 -97
- data/app/models/zuora_connect/zuora_user.rb +1 -1
- data/config/initializers/postgresql_adapter.rb +71 -1
- data/config/initializers/prometheus.rb +80 -23
- data/config/initializers/redis.rb +4 -4
- data/config/initializers/resque.rb +20 -6
- data/config/initializers/unicorn.rb +30 -2
- data/config/routes.rb +5 -1
- data/lib/metrics/net.rb +7 -7
- data/lib/middleware/json_parse_errors.rb +13 -2
- data/lib/middleware/metrics_middleware.rb +52 -38
- data/lib/resque/dynamic_queues.rb +1 -1
- data/lib/resque/plugins/app_instance_job.rb +6 -10
- data/lib/zuora_connect.rb +6 -63
- data/lib/zuora_connect/controllers/helpers.rb +212 -76
- data/lib/zuora_connect/engine.rb +2 -1
- data/lib/zuora_connect/railtie.rb +9 -53
- data/lib/zuora_connect/version.rb +1 -1
- data/lib/zuora_connect/zuora_audit.rb +31 -0
- metadata +19 -7
- data/app/models/zuora_connect/telegraf.rb +0 -97
- data/lib/logging/connect_formatter.rb +0 -44
- data/lib/metrics/influx/point_value.rb +0 -79
@@ -1,9 +1,15 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
3
|
class PostgreSQLAdapter < AbstractAdapter
|
4
|
+
|
5
|
+
SCHEMA_ADDITIONAL_TYPES = 'SchemaAdditionalTypes'.freeze
|
6
|
+
|
4
7
|
private
|
5
|
-
def
|
8
|
+
def load_additional_types_latest(oids = nil)
|
6
9
|
initializer = OID::TypeMapInitializer.new(type_map)
|
10
|
+
|
11
|
+
return if loaded_from_cache?(initializer)
|
12
|
+
|
7
13
|
if supports_ranges?
|
8
14
|
query = <<-SQL
|
9
15
|
SELECT DISTINCT on (t.typname) t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
@@ -24,9 +30,73 @@ module ActiveRecord
|
|
24
30
|
end
|
25
31
|
|
26
32
|
execute_and_clear(query, "SCHEMA", []) do |records|
|
33
|
+
cache_additional_types(records)
|
27
34
|
initializer.run(records)
|
28
35
|
end
|
29
36
|
end
|
37
|
+
|
38
|
+
def load_additional_types_deprecated(type_map, oids = nil)
|
39
|
+
initializer = OID::TypeMapInitializer.new(type_map)
|
40
|
+
|
41
|
+
return if loaded_from_cache?(initializer)
|
42
|
+
|
43
|
+
if supports_ranges?
|
44
|
+
query = <<-SQL
|
45
|
+
SELECT DISTINCT on (t.typname) t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
46
|
+
FROM pg_type as t
|
47
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
48
|
+
SQL
|
49
|
+
else
|
50
|
+
query = <<-SQL
|
51
|
+
SELECT DISTINCT on (t.typname) t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
52
|
+
FROM pg_type as t
|
53
|
+
SQL
|
54
|
+
end
|
55
|
+
|
56
|
+
if oids
|
57
|
+
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
58
|
+
else
|
59
|
+
query += initializer.query_conditions_for_initial_load(type_map)
|
60
|
+
end
|
61
|
+
|
62
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
63
|
+
cache_additional_types(records)
|
64
|
+
initializer.run(records)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def loaded_from_cache?(initializer)
|
69
|
+
if defined?(Redis.current)
|
70
|
+
begin
|
71
|
+
if Redis.current.exists(SCHEMA_ADDITIONAL_TYPES)
|
72
|
+
initializer.run(JSON.parse(Redis.current.get(SCHEMA_ADDITIONAL_TYPES)))
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
rescue => ex
|
76
|
+
Rails.logger.warn('Exception occurred while loading additional types', ex)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
def cache_additional_types(records)
|
84
|
+
if defined?(Redis.current)
|
85
|
+
begin
|
86
|
+
Redis.current.setex(SCHEMA_ADDITIONAL_TYPES, 1.hour.to_i, records.to_json)
|
87
|
+
rescue => ex
|
88
|
+
Rails.logger.warn('Exception occurred while caching additional types', ex)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
rails_version = Rails.version.split('.').map { |x| x.to_i }
|
95
|
+
if (rails_version <=> [5, 2, 0]) >= 1
|
96
|
+
alias :load_additional_types :load_additional_types_latest
|
97
|
+
else
|
98
|
+
alias :load_additional_types :load_additional_types_deprecated
|
99
|
+
end
|
30
100
|
end
|
31
101
|
end
|
32
102
|
end
|
@@ -3,38 +3,95 @@ if defined? Prometheus
|
|
3
3
|
require "zuora_connect/version"
|
4
4
|
require "zuora_api/version"
|
5
5
|
|
6
|
+
resque_path = "#{ENV['RESQUE_EXPORTER_PATH'] || Rails.root.join('tmp/resque_exporter')}/*.prom"
|
7
|
+
prometheus_path = Rails.root.join("tmp/prometheus")
|
8
|
+
|
9
|
+
Dir[resque_path, "#{prometheus_path}/*.bin"].each do |file_path|
|
10
|
+
File.unlink(file_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'prometheus/client/data_stores/direct_file_store'
|
14
|
+
Prometheus::Client.config.data_store = Prometheus::Client::DataStores::DirectFileStore.new(
|
15
|
+
dir: prometheus_path
|
16
|
+
)
|
17
|
+
|
18
|
+
class ResqueExporter
|
19
|
+
require 'prometheus/client/formats/text'
|
20
|
+
require 'fileutils'
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@lock = Monitor.new
|
24
|
+
@registry = Prometheus::Client.registry
|
25
|
+
@path = ENV['RESQUE_EXPORTER_PATH'] || Rails.root.join('tmp/resque_exporter')
|
26
|
+
FileUtils.mkdir_p(@path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def export
|
30
|
+
filename = File.join(@path, 'resque_export.prom')
|
31
|
+
@lock.synchronize do
|
32
|
+
File.open(filename, 'w+') do |file|
|
33
|
+
file.write(Prometheus::Client::Formats::Text.marshal(@registry))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
rescue
|
37
|
+
# Ignored
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
most_recent_aggregation = {}
|
42
|
+
sum_aggregation = {}
|
43
|
+
if defined?(Unicorn)
|
44
|
+
most_recent_aggregation[:aggregation] = :most_recent
|
45
|
+
sum_aggregation[:aggregation] = :sum
|
46
|
+
end
|
47
|
+
|
6
48
|
# Create a default Prometheus registry for our metrics.
|
7
49
|
prometheus = Prometheus::Client.registry
|
8
50
|
|
9
51
|
# Create your metrics.
|
10
|
-
ZUORA_VERSION =
|
11
|
-
CONNECT_VERSION =
|
12
|
-
RAILS_VERSION =
|
13
|
-
RUBY_V =
|
52
|
+
ZUORA_VERSION = prometheus.gauge(:zuora_version, docstring: 'The current Zuora Gem version.', labels: %i(version name), preset_labels: { version: ZuoraAPI::VERSION, name: ZuoraObservability::Env.app_name }, store_settings: most_recent_aggregation)
|
53
|
+
CONNECT_VERSION = prometheus.gauge(:gem_version, docstring: 'The current Connect Gem version.', labels: %i(version name), preset_labels: { version: ZuoraConnect::VERSION, name: ZuoraObservability::Env.app_name }, store_settings: most_recent_aggregation)
|
54
|
+
RAILS_VERSION = prometheus.gauge(:rails_version, docstring: 'The current Rails version.', labels: %i(version name), preset_labels: { version: Rails.version, name: ZuoraObservability::Env.app_name }, store_settings: most_recent_aggregation)
|
55
|
+
RUBY_V = prometheus.gauge(:ruby_version, docstring: 'The current Ruby version.', labels: %i(version name), preset_labels: { version: RUBY_VERSION, name: ZuoraObservability::Env.app_name }, store_settings: most_recent_aggregation)
|
14
56
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
prometheus.register(RUBY_V);RUBY_V.set({version: RUBY_VERSION, name: ZuoraConnect::Telegraf.app_name},0)
|
57
|
+
ZUORA_VERSION.set(0)
|
58
|
+
CONNECT_VERSION.set(0)
|
59
|
+
RAILS_VERSION.set(0)
|
60
|
+
RUBY_V.set(0)
|
20
61
|
|
21
62
|
# Do they have resque jobs?
|
22
63
|
if defined? Resque.redis
|
23
|
-
REDIS_CONNECTION =
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
prometheus.register(REDIS_CONNECTION)
|
31
|
-
prometheus.register(FINISHED_JOBS)
|
32
|
-
prometheus.register(ACTIVE_WORKERS)
|
33
|
-
prometheus.register(WORKERS)
|
34
|
-
prometheus.register(FAILED_JOBS)
|
35
|
-
prometheus.register(PENDING_JOBS)
|
36
|
-
|
64
|
+
REDIS_CONNECTION = prometheus.gauge(:redis_connection, docstring: 'The status of the redis connection, 0 or 1', labels: %i(connection name), preset_labels: {connection:'redis', name: ZuoraObservability::Env.app_name}, store_settings: most_recent_aggregation)
|
65
|
+
JOBS_FINISHED = prometheus.gauge(:jobs_finished, docstring: 'Done resque jobs', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraObservability::Env.app_name}, store_settings: most_recent_aggregation)
|
66
|
+
WORKERS_TOTAL = prometheus.gauge(:workers_total, docstring: 'Total resque workers', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraObservability::Env.app_name}, store_settings: most_recent_aggregation)
|
67
|
+
WORKERS_ACTIVE = prometheus.gauge(:workers_active, docstring: 'Active resque workers', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraObservability::Env.app_name}, store_settings: most_recent_aggregation)
|
68
|
+
JOBS_FAILED = prometheus.gauge(:jobs_failed, docstring: 'Failed resque jobs', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraObservability::Env.app_name}, store_settings: most_recent_aggregation)
|
69
|
+
JOBS_PENDING = prometheus.gauge(:jobs_pending, docstring: 'Pending resque jobs', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraObservability::Env.app_name}, store_settings: most_recent_aggregation)
|
37
70
|
end
|
38
71
|
|
72
|
+
if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
|
73
|
+
UNICORN_KILLS = prometheus.gauge(
|
74
|
+
:unicorn_kills,
|
75
|
+
docstring: 'Unicorn Kills',
|
76
|
+
labels: %i(type name),
|
77
|
+
preset_labels: {type:'Unicorn-Killer', name: ZuoraObservability::Env.app_name},
|
78
|
+
store_settings: sum_aggregation
|
79
|
+
)
|
80
|
+
|
81
|
+
ZuoraObservability::Metrics.unicorn_listener.each do |key, _|
|
82
|
+
gauge_name = "unicorn_#{key}".gsub(/[^a-zA-Z0-9_]/, '_')
|
83
|
+
gauge = prometheus.gauge(
|
84
|
+
gauge_name.to_sym,
|
85
|
+
docstring: 'Unicorn Stats',
|
86
|
+
labels: %i(type name),
|
87
|
+
preset_labels: { type: 'unicorn', name: ZuoraObservability::Env.app_name },
|
88
|
+
store_settings: most_recent_aggregation
|
89
|
+
)
|
90
|
+
Prometheus.const_set(
|
91
|
+
gauge_name.upcase,
|
92
|
+
gauge
|
93
|
+
)
|
94
|
+
end
|
95
|
+
end
|
39
96
|
end
|
40
97
|
end
|
@@ -12,21 +12,21 @@ class RedisFlash
|
|
12
12
|
end
|
13
13
|
|
14
14
|
if defined?(Redis.current)
|
15
|
-
Redis.current = Redis.new(:id => "#{
|
15
|
+
Redis.current = Redis.new(:id => "#{ZuoraObservability::Env.full_process_name(process_name: 'Redis')}", :url => redis_url, :timeout => 6, :reconnect_attempts => 2)
|
16
16
|
browser_urls['Redis'] = { "url" => redis_url }
|
17
17
|
if defined?(Resque.redis)
|
18
18
|
if resque_url != redis_url
|
19
|
-
Resque.redis = Redis.new(:id => "#{
|
19
|
+
Resque.redis = Redis.new(:id => "#{ZuoraObservability::Env.full_process_name(process_name: 'Resque')}", :url => resque_url, :timeout => 6, :reconnect_attempts => 2)
|
20
20
|
browser_urls['Resque'] = { "url" => resque_url }
|
21
21
|
else
|
22
22
|
Resque.redis = Redis.current
|
23
23
|
end
|
24
24
|
end
|
25
25
|
if defined?(flash_url.present?)
|
26
|
-
RedisFlash.current = Redis.new(:id => "#{
|
26
|
+
RedisFlash.current = Redis.new(:id => "#{ZuoraObservability::Env.full_process_name(process_name: 'Flash')}", :url => flash_url, :timeout => 6, :reconnect_attempts => 2)
|
27
27
|
browser_urls['Flash'] = { "url" => flash_url }
|
28
28
|
end
|
29
29
|
end
|
30
30
|
if defined?(RedisBrowser)
|
31
31
|
RedisBrowser.configure("connections" => browser_urls)
|
32
|
-
end
|
32
|
+
end
|
@@ -5,6 +5,20 @@ if defined?(Resque::Worker)
|
|
5
5
|
Resque::Job.send(:include, Resque::SelfLookup)
|
6
6
|
end
|
7
7
|
|
8
|
+
if defined?(Resque::Job) && defined?(Prometheus)
|
9
|
+
module ResquePrometheusExtensions
|
10
|
+
EXPORTER = Prometheus::ResqueExporter.new
|
11
|
+
def perform
|
12
|
+
super
|
13
|
+
EXPORTER.export
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Resque::Job
|
18
|
+
prepend ResquePrometheusExtensions
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
8
22
|
Resque.module_eval do
|
9
23
|
# Returns a hash, mapping queue names to queue sizes
|
10
24
|
def queue_sizes
|
@@ -22,13 +36,13 @@ Resque.module_eval do
|
|
22
36
|
end
|
23
37
|
|
24
38
|
if defined?(Resque.logger)
|
25
|
-
Resque.logger =
|
26
|
-
Resque::Scheduler.logger =
|
39
|
+
Resque.logger = ZuoraObservability::Logger.custom_logger(name: "Resque", type: 'Monologger', level: MonoLogger::INFO)
|
40
|
+
Resque::Scheduler.logger = ZuoraObservability::Logger.custom_logger(name: "ResqueScheduler") if defined?(Resque::Scheduler)
|
27
41
|
end
|
28
42
|
if defined?(Delayed::Worker.logger)
|
29
|
-
Delayed::Worker.logger =
|
43
|
+
Delayed::Worker.logger = ZuoraObservability::Logger.custom_logger(name: "DelayedJob", type: 'Monologger', level: MonoLogger::INFO)
|
30
44
|
end
|
31
45
|
|
32
|
-
Makara::Logging::Logger.logger =
|
33
|
-
ElasticAPM.agent.config.logger =
|
34
|
-
ActionMailer::Base.logger =
|
46
|
+
Makara::Logging::Logger.logger = ZuoraObservability::Logger.custom_logger(name: "Makara") if defined?(Makara)
|
47
|
+
ElasticAPM.agent.config.logger = ZuoraObservability::Logger.custom_logger(name: "ElasticAPM", level: MonoLogger::WARN) if defined?(ElasticAPM) && ElasticAPM.running?
|
48
|
+
ActionMailer::Base.logger = ZuoraObservability::Logger.custom_logger(name: "ActionMailer", type: 'Monologger') if defined?(ActionMailer)
|
@@ -3,7 +3,35 @@ if defined?(Unicorn::WorkerKiller)
|
|
3
3
|
self.singleton_class.send(:alias_method, :kill_self_old, :kill_self)
|
4
4
|
def self.kill_self(logger, start_time)
|
5
5
|
self.kill_self_old(logger, start_time)
|
6
|
-
|
6
|
+
if defined?(Prometheus)
|
7
|
+
Prometheus::UNICORN_KILLS.set(1)
|
8
|
+
else
|
9
|
+
ZuoraObservability::Metrics.write_to_telegraf(direction: 'Unicorn-Killer', tags: {app_instance: 0}, values: {kill: 1})
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
9
|
-
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if defined?(Unicorn::HttpServer) && defined?(Prometheus)
|
16
|
+
module HttpServerExtensions
|
17
|
+
def kill_worker(signal, wpid)
|
18
|
+
Prometheus::UNICORN_KILLS.increment
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module WorkerExtensions
|
24
|
+
def soft_kill(sig)
|
25
|
+
Prometheus::UNICORN_KILLS.increment
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Unicorn::HttpServer
|
31
|
+
prepend HttpServerExtensions
|
32
|
+
end
|
33
|
+
|
34
|
+
class Unicorn::Worker
|
35
|
+
prepend WorkerExtensions
|
36
|
+
end
|
37
|
+
end
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
ZuoraConnect::Engine.routes.draw do
|
2
2
|
get '/health' => 'static#health'
|
3
|
-
get '/internal/data' => 'static#metrics'
|
4
3
|
post '/initialize_app' => 'static#initialize_app'
|
5
4
|
|
5
|
+
if ENV['PROVISION_USER'].present? && ENV['PROVISION_SECRET'].present?
|
6
|
+
post '/provision' => 'static#provision'
|
7
|
+
get '/instance/:id/user' => 'static#instance_user'
|
8
|
+
end
|
9
|
+
|
6
10
|
namespace :api do
|
7
11
|
namespace :v1 do
|
8
12
|
resources :app_instance, :only => [:index], defaults: {format: :json} do
|
data/lib/metrics/net.rb
CHANGED
@@ -68,12 +68,12 @@ class HttpLogger
|
|
68
68
|
log_request_body(request)
|
69
69
|
log_request_headers(request)
|
70
70
|
if defined?(response) && response
|
71
|
-
tags = tags.merge({status: response.code.to_i})
|
71
|
+
tags = tags.merge({status: response.code.to_i})
|
72
72
|
log_response_code(response)
|
73
73
|
log_response_headers(response)
|
74
74
|
log_response_body(response.body)
|
75
75
|
end
|
76
|
-
|
76
|
+
ZuoraObservability::Metrics.write_to_telegraf(direction: :outbound, tags: tags, values: values)
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -95,7 +95,7 @@ class HttpLogger
|
|
95
95
|
end
|
96
96
|
|
97
97
|
HTTP_METHODS_WITH_BODY = Set.new(%w(POST PUT GET PATCH))
|
98
|
-
|
98
|
+
|
99
99
|
def log_request_body(request)
|
100
100
|
if self.class.log_request_body
|
101
101
|
if HTTP_METHODS_WITH_BODY.include?(request.method)
|
@@ -149,8 +149,8 @@ class HttpLogger
|
|
149
149
|
def truncate_body(body)
|
150
150
|
if collapse_body_limit && collapse_body_limit > 0 && body && body.size >= collapse_body_limit
|
151
151
|
body_piece_size = collapse_body_limit / 2
|
152
|
-
body[0..body_piece_size] +
|
153
|
-
"\n\n<some data truncated>\n\n" +
|
152
|
+
body[0..body_piece_size] +
|
153
|
+
"\n\n<some data truncated>\n\n" +
|
154
154
|
body[(body.size - body_piece_size)..body.size]
|
155
155
|
else
|
156
156
|
body
|
@@ -203,7 +203,7 @@ class Net::HTTP
|
|
203
203
|
|
204
204
|
def request(request, body = nil, &block)
|
205
205
|
HttpLogger.perform(self, request, body) do
|
206
|
-
request_without_logging(request, body, &block)
|
206
|
+
request_without_logging(request, body, &block)
|
207
207
|
end
|
208
208
|
end
|
209
209
|
end
|
@@ -215,4 +215,4 @@ if defined?(Rails)
|
|
215
215
|
HttpLogger.logger = ZuoraConnect.logger unless HttpLogger.logger
|
216
216
|
end
|
217
217
|
end
|
218
|
-
end
|
218
|
+
end
|
@@ -7,7 +7,7 @@ module ZuoraConnect
|
|
7
7
|
def call(env)
|
8
8
|
begin
|
9
9
|
@app.call(env)
|
10
|
-
rescue
|
10
|
+
rescue DynamicRailsError => error
|
11
11
|
if env['HTTP_ACCEPT'] =~ /application\/json/ || env['CONTENT_TYPE'] =~ /application\/json/
|
12
12
|
return [
|
13
13
|
400, { "Content-Type" => "application/json" },
|
@@ -18,5 +18,16 @@ module ZuoraConnect
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
# Note(hartley): remove once the minimum supported version of Rails is 5.2
|
23
|
+
class DynamicRailsError < StandardError
|
24
|
+
def self.===(exception)
|
25
|
+
if Rails.version >= "5.2"
|
26
|
+
exception.is_a?(ActionDispatch::Http::Parameters::ParseError)
|
27
|
+
else
|
28
|
+
exception.is_a?(ActionDispatch::ParamsParser::ParseError)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
21
32
|
end
|
22
|
-
end
|
33
|
+
end
|
@@ -17,7 +17,7 @@ module ZuoraConnect
|
|
17
17
|
# payloads with 500 requests do not have status as it is not set by the controller
|
18
18
|
# https://github.com/rails/rails/issues/33335
|
19
19
|
#status_code = payload[:status] ? payload[:status] : payload[:exception_object].present? ? 500 : ""
|
20
|
-
if payload[:exception].present?
|
20
|
+
if payload[:exception].present?
|
21
21
|
status_code, exception = [500, payload[:exception].first]
|
22
22
|
else
|
23
23
|
status_code, exception = [payload[:status], nil]
|
@@ -28,7 +28,7 @@ module ZuoraConnect
|
|
28
28
|
values = {view_time: payload[:view_runtime], db_time: payload[:db_runtime], response_time: ((finished-started)*1000)}.compact
|
29
29
|
values = values.map{ |k,v| [k,v.round(2)]}.to_h
|
30
30
|
|
31
|
-
|
31
|
+
ZuoraObservability::Metrics.write_to_telegraf(direction: :inbound, tags: tags, values: values)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -42,7 +42,7 @@ module ZuoraConnect
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def call(env)
|
45
|
-
@bad_headers = ["HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_HOST", "HTTP_X_FORWARDED_PORT", "HTTP_X_FORWARDED_PROTO", "HTTP_X_FORWARDED_SCHEME", "HTTP_X_FORWARDED_SSL"]
|
45
|
+
@bad_headers = ["HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_HOST", "HTTP_X_FORWARDED_PORT", "HTTP_X_FORWARDED_PROTO", "HTTP_X_FORWARDED_SCHEME", "HTTP_X_FORWARDED_SSL"]
|
46
46
|
if !ActionDispatch::Request::HTTP_METHODS.include?(env["REQUEST_METHOD"].upcase)
|
47
47
|
[405, {"Content-Type" => "text/plain"}, ["Method Not Allowed"]]
|
48
48
|
else
|
@@ -64,46 +64,33 @@ module ZuoraConnect
|
|
64
64
|
#Remove bad headers
|
65
65
|
@bad_headers.each { |header| env.delete(header) }
|
66
66
|
|
67
|
+
if defined?(Prometheus) && env['PATH_INFO'] == '/connect/internal/metrics'
|
68
|
+
# Prometheus Stuff
|
69
|
+
metrics = ZuoraObservability::Metrics.resque
|
70
|
+
redis_up = metrics.present? && metrics.dig(:Resque, :Workers_Total).present? ? 1 : 0
|
71
|
+
Prometheus::REDIS_CONNECTION.set(redis_up)
|
72
|
+
|
73
|
+
process_prometheus_metric(metrics: metrics)
|
74
|
+
|
75
|
+
if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
|
76
|
+
ZuoraObservability::Metrics.unicorn_listener.each do |key, value|
|
77
|
+
gauge = Prometheus.const_get("unicorn_#{key}".gsub(/[^a-zA-Z0-9_]/, '_').upcase)
|
78
|
+
gauge.set(value) if gauge.present?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
67
83
|
#Thread.current[:appinstance] = nil
|
68
84
|
start_time = Time.now
|
69
85
|
begin
|
70
86
|
@status, @headers, @response = @app.call(env)
|
71
|
-
ensure
|
72
|
-
|
87
|
+
ensure
|
88
|
+
|
73
89
|
# If the url contains any CSS or JavaScript files then do not collect metrics for them
|
74
90
|
if ["css", "assets", "jpg", "png", "jpeg", "ico"].any? { |word| env['PATH_INFO'].include?(word) } || /.*\.js$/.match(env['PATH_INFO'])
|
75
91
|
tags = {status: @status, controller: 'ActionController', action: 'Assets', app_instance: 0}
|
76
92
|
values = {response_time: ((Time.now - start_time)*1000).round(2) }
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
if defined? Prometheus
|
81
|
-
#Prometheus Stuff
|
82
|
-
if env['PATH_INFO'] == '/connect/internal/metrics'
|
83
|
-
|
84
|
-
#Do something before each scrape
|
85
|
-
if defined? Resque.redis
|
86
|
-
begin
|
87
|
-
|
88
|
-
Resque.redis.ping
|
89
|
-
|
90
|
-
Prometheus::REDIS_CONNECTION.set({connection:'redis',name: ZuoraConnect::Telegraf.app_name},1)
|
91
|
-
Prometheus::FINISHED_JOBS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:processed])
|
92
|
-
Prometheus::PENDING_JOBS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:pending])
|
93
|
-
Prometheus::ACTIVE_WORKERS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:working])
|
94
|
-
Prometheus::WORKERS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:workers])
|
95
|
-
Prometheus::FAILED_JOBS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:failed])
|
96
|
-
|
97
|
-
rescue Redis::CannotConnectError
|
98
|
-
Prometheus::REDIS_CONNECTION.set({connection:'redis',name: ZuoraConnect::Telegraf.app_name},0)
|
99
|
-
end
|
100
|
-
|
101
|
-
if ZuoraConnect.configuration.custom_prometheus_update_block != nil
|
102
|
-
ZuoraConnect.configuration.custom_prometheus_update_block.call()
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
93
|
+
ZuoraObservability::Metrics.write_to_telegraf(direction: 'request-inbound-assets', tags: tags, values: values)
|
107
94
|
end
|
108
95
|
|
109
96
|
# Uncomment following block of code for handling engine requests/requests without controller
|
@@ -119,13 +106,13 @@ module ZuoraConnect
|
|
119
106
|
content_type = @headers['Content-Type'].split(';')[0] if @headers['Content-Type']
|
120
107
|
content_type = content_type.gsub('text/javascript', 'application/javascript')
|
121
108
|
tags = {status: @status, content_type: content_type}
|
122
|
-
|
109
|
+
|
123
110
|
tags = tags.merge({controller: 'ActionController'})
|
124
111
|
tags = tags.merge({action: 'RoutingError' }) if @status == 404
|
125
|
-
|
112
|
+
|
126
113
|
values = {response_time: ((Time.now - start_time)*1000).round(2) }
|
127
114
|
|
128
|
-
|
115
|
+
ZuoraObservability::Metrics.write_to_telegraf(direction: :inbound, tags: tags, values: values)
|
129
116
|
end
|
130
117
|
end
|
131
118
|
Thread.current[:inbound_metric] = nil
|
@@ -133,5 +120,32 @@ module ZuoraConnect
|
|
133
120
|
[@status, @headers, @response]
|
134
121
|
end
|
135
122
|
end
|
123
|
+
|
124
|
+
def process_prometheus_metric(type: 'none', metrics: {})
|
125
|
+
return if metrics.blank?
|
126
|
+
|
127
|
+
prometheus = Prometheus::Client.registry
|
128
|
+
most_recent_aggregation = {}
|
129
|
+
if Prometheus::Client.config.data_store.is_a?(Prometheus::Client::DataStores::DirectFileStore)
|
130
|
+
most_recent_aggregation[:aggregation] = :most_recent
|
131
|
+
end
|
132
|
+
metrics.each do |key, value|
|
133
|
+
next if %w[app_name url].include?(key.to_s)
|
134
|
+
|
135
|
+
if value.is_a?(Hash)
|
136
|
+
process_prometheus_metric(type: key.to_s, metrics: value)
|
137
|
+
else
|
138
|
+
gauge_name = key.to_s.downcase.gsub(/[^a-z0-9_]/, '_')
|
139
|
+
gauge = prometheus.get(gauge_name.to_sym) || prometheus.gauge(
|
140
|
+
gauge_name.to_sym,
|
141
|
+
docstring: "#{key} metric",
|
142
|
+
labels: %i(type name),
|
143
|
+
preset_labels: { type: type, name: ZuoraObservability::Env.app_name },
|
144
|
+
store_settings: most_recent_aggregation
|
145
|
+
)
|
146
|
+
gauge.set(value)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
136
150
|
end
|
137
151
|
end
|