zuora_connect 2.1.1 → 3.0.0.pre.e
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/app/controllers/zuora_connect/static_controller.rb +84 -17
- data/app/models/zuora_connect/app_instance_base.rb +175 -106
- 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 +14 -12
- 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 +48 -72
- 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 +229 -89
- data/lib/zuora_connect/engine.rb +2 -1
- data/lib/zuora_connect/railtie.rb +6 -64
- 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
|
@@ -20,15 +34,3 @@ Resque.module_eval do
|
|
20
34
|
Hash[queue_names.zip(sizes)]
|
21
35
|
end
|
22
36
|
end
|
23
|
-
|
24
|
-
if defined?(Resque.logger)
|
25
|
-
Resque.logger = ZuoraConnect.custom_logger(name: "Resque", type: 'Monologger', level: MonoLogger::INFO)
|
26
|
-
Resque::Scheduler.logger = ZuoraConnect.custom_logger(name: "ResqueScheduler") if defined?(Resque::Scheduler)
|
27
|
-
end
|
28
|
-
if defined?(Delayed::Worker.logger)
|
29
|
-
Delayed::Worker.logger = ZuoraConnect.custom_logger(name: "DelayedJob", type: 'Monologger', level: MonoLogger::INFO)
|
30
|
-
end
|
31
|
-
|
32
|
-
Makara::Logging::Logger.logger = ZuoraConnect.custom_logger(name: "Makara") if defined?(Makara)
|
33
|
-
ElasticAPM.agent.config.logger = ZuoraConnect.custom_logger(name: "ElasticAPM", level: MonoLogger::WARN) if defined?(ElasticAPM) && ElasticAPM.running?
|
34
|
-
ActionMailer::Base.logger = ZuoraConnect.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
|
@@ -1,37 +1,6 @@
|
|
1
1
|
module ZuoraConnect
|
2
2
|
require 'uri'
|
3
3
|
|
4
|
-
# Object of this class is passed to the ActiveSupport::Notification hook
|
5
|
-
class PageRequest
|
6
|
-
|
7
|
-
# This method is triggered when a non error page is loaded (not 404)
|
8
|
-
def call(name, started, finished, unique_id, payload)
|
9
|
-
# If the url contains any css or JavaScript files then do not collect metrics for them
|
10
|
-
return nil if ["css", "assets", "jpg", "png", "jpeg", "ico"].any? { |word| payload[:path].include?(word) }
|
11
|
-
|
12
|
-
# Getting the endpoint and the content_type
|
13
|
-
content_hash = {:html => "text/html", :js => "application/javascript", :json => "application/json", :csv => "text/csv"}
|
14
|
-
content_type = content_hash.key?(payload[:format]) ? content_hash[payload[:format]] : payload[:format]
|
15
|
-
content_type = content_type.to_s.gsub('text/javascript', 'application/javascript')
|
16
|
-
|
17
|
-
# payloads with 500 requests do not have status as it is not set by the controller
|
18
|
-
# https://github.com/rails/rails/issues/33335
|
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
|
25
|
-
|
26
|
-
tags = {method: payload[:method], status: status_code, error_type: exception, content_type: content_type, controller: payload[:controller], action: payload[:action]}.compact
|
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)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
4
|
class MetricsMiddleware
|
36
5
|
|
37
6
|
require "zuora_connect/version"
|
@@ -42,7 +11,7 @@ module ZuoraConnect
|
|
42
11
|
end
|
43
12
|
|
44
13
|
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"]
|
14
|
+
@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
15
|
if !ActionDispatch::Request::HTTP_METHODS.include?(env["REQUEST_METHOD"].upcase)
|
47
16
|
[405, {"Content-Type" => "text/plain"}, ["Method Not Allowed"]]
|
48
17
|
else
|
@@ -64,47 +33,27 @@ module ZuoraConnect
|
|
64
33
|
#Remove bad headers
|
65
34
|
@bad_headers.each { |header| env.delete(header) }
|
66
35
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
# If the url contains any CSS or JavaScript files then do not collect metrics for them
|
74
|
-
if ["css", "assets", "jpg", "png", "jpeg", "ico"].any? { |word| env['PATH_INFO'].include?(word) } || /.*\.js$/.match(env['PATH_INFO'])
|
75
|
-
tags = {status: @status, controller: 'ActionController', action: 'Assets', app_instance: 0}
|
76
|
-
values = {response_time: ((Time.now - start_time)*1000).round(2) }
|
77
|
-
ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: 'request-inbound-assets', tags: tags, values: values)
|
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])
|
36
|
+
if defined?(Prometheus) && env['PATH_INFO'] == '/connect/internal/metrics'
|
37
|
+
# Prometheus Stuff
|
38
|
+
metrics = ZuoraObservability::Metrics.resque
|
39
|
+
redis_up = metrics.present? && metrics.dig(:Resque, :Workers_Total).present? ? 1 : 0
|
40
|
+
Prometheus::REDIS_CONNECTION.set(redis_up)
|
96
41
|
|
97
|
-
|
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
|
42
|
+
process_prometheus_metric(metrics: metrics)
|
105
43
|
|
44
|
+
if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
|
45
|
+
ZuoraObservability::Metrics.unicorn_listener.each do |key, value|
|
46
|
+
gauge = Prometheus.const_get("unicorn_#{key}".gsub(/[^a-zA-Z0-9_]/, '_').upcase)
|
47
|
+
gauge.set(value) if gauge.present?
|
106
48
|
end
|
107
49
|
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#Thread.current[:appinstance] = nil
|
53
|
+
start_time = Time.now
|
54
|
+
begin
|
55
|
+
@status, @headers, @response = @app.call(env)
|
56
|
+
ensure
|
108
57
|
|
109
58
|
# Uncomment following block of code for handling engine requests/requests without controller
|
110
59
|
# else
|
@@ -119,13 +68,13 @@ module ZuoraConnect
|
|
119
68
|
content_type = @headers['Content-Type'].split(';')[0] if @headers['Content-Type']
|
120
69
|
content_type = content_type.gsub('text/javascript', 'application/javascript')
|
121
70
|
tags = {status: @status, content_type: content_type}
|
122
|
-
|
71
|
+
|
123
72
|
tags = tags.merge({controller: 'ActionController'})
|
124
73
|
tags = tags.merge({action: 'RoutingError' }) if @status == 404
|
125
|
-
|
74
|
+
|
126
75
|
values = {response_time: ((Time.now - start_time)*1000).round(2) }
|
127
76
|
|
128
|
-
|
77
|
+
ZuoraObservability::Metrics.write_to_telegraf(direction: :inbound, tags: tags, values: values)
|
129
78
|
end
|
130
79
|
end
|
131
80
|
Thread.current[:inbound_metric] = nil
|
@@ -133,5 +82,32 @@ module ZuoraConnect
|
|
133
82
|
[@status, @headers, @response]
|
134
83
|
end
|
135
84
|
end
|
85
|
+
|
86
|
+
def process_prometheus_metric(type: 'none', metrics: {})
|
87
|
+
return if metrics.blank?
|
88
|
+
|
89
|
+
prometheus = Prometheus::Client.registry
|
90
|
+
most_recent_aggregation = {}
|
91
|
+
if Prometheus::Client.config.data_store.is_a?(Prometheus::Client::DataStores::DirectFileStore)
|
92
|
+
most_recent_aggregation[:aggregation] = :most_recent
|
93
|
+
end
|
94
|
+
metrics.each do |key, value|
|
95
|
+
next if %w[app_name url].include?(key.to_s)
|
96
|
+
|
97
|
+
if value.is_a?(Hash)
|
98
|
+
process_prometheus_metric(type: key.to_s, metrics: value)
|
99
|
+
else
|
100
|
+
gauge_name = key.to_s.downcase.gsub(/[^a-z0-9_]/, '_')
|
101
|
+
gauge = prometheus.get(gauge_name.to_sym) || prometheus.gauge(
|
102
|
+
gauge_name.to_sym,
|
103
|
+
docstring: "#{key} metric",
|
104
|
+
labels: %i(type name),
|
105
|
+
preset_labels: { type: type, name: ZuoraObservability::Env.app_name },
|
106
|
+
store_settings: most_recent_aggregation
|
107
|
+
)
|
108
|
+
gauge.set(value)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
136
112
|
end
|
137
113
|
end
|