Class: ZuoraConnect::MetricsMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/middleware/metrics_middleware.rb

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ MetricsMiddleware

Returns a new instance of MetricsMiddleware.

[View source]

40
41
42
# File 'lib/middleware/metrics_middleware.rb', line 40

def initialize(app)
  @app = app
end

Instance Method Details

#call(env) ⇒ Object

[View source]

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/middleware/metrics_middleware.rb', line 44

def call(env)
  @bad_headers = ["HTTP_X_FORWARDED_HOST", "HTTP_X_FORWARDED_PORT", "HTTP_X_FORWARDED_PROTO", "HTTP_X_FORWARDED_SCHEME", "HTTP_X_FORWARDED_SSL"]
  if !ActionDispatch::Request::HTTP_METHODS.include?(env["REQUEST_METHOD"].upcase)
    [405, {"Content-Type" => "text/plain"}, ["Method Not Allowed"]]
  else
    if Thread.current[:isHallway]
      # We need the forwarded host header to identify location of tenant
      whitelist = Regexp.new(".*[\.]zuora[\.]com$|^zuora[\.]com$")
      @bad_headers.delete('HTTP_X_FORWARDED_HOST') if whitelist.match(env['HTTP_X_FORWARDED_HOST']).present?
    end

    #Remove bad headers
    @bad_headers.each { |header| env.delete(header) }

    if defined?(Prometheus) && env['PATH_INFO'] == '/connect/internal/metrics'

      # Prometheus Stuff
      metrics = ZuoraObservability::Metrics.resque
      metrics = defined?(ZuoraConnect::AppInstance.get_metrics) ? ZuoraConnect::AppInstance.get_metrics(metrics) : metrics

      redis_up = metrics.present? && metrics.dig(:Resque, :Workers_Total).present? ? 1 : 0
      Prometheus::REDIS_CONNECTION.set(redis_up)

      process_prometheus_metric(metrics: metrics)

      if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
        ZuoraObservability::Metrics.unicorn_listener.each do |key, value|
          gauge = Prometheus.const_get("unicorn_#{key}".gsub(/[^a-zA-Z0-9_]/, '_').upcase)
          gauge.set(value) if gauge.present?
        end
      end
    end

    #Thread.current[:appinstance] = nil
    start_time = Time.now
    begin
      @status, @headers, @response = @app.call(env)
    ensure
      # Writing to telegraf: Handle 404
      if [404, 500].include?(@status)
        content_type = @headers['Content-Type'].split(';')[0] if @headers['Content-Type']
        content_type = content_type.gsub('text/javascript', 'application/javascript')
        tags = { status: @status, content_type: content_type }

        tags = tags.merge({ controller: 'ActionController' })
        tags = tags.merge({ action: 'RoutingError' }) if @status == 404

        values = { response_time: ((Time.now - start_time) * 1000).round(2) }

        ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: :inbound, tags: tags, values: values)
      end
      Thread.current[:inbound_metric] = nil
    end
    [@status, @headers, @response]
  end
end

#process_prometheus_metric(type: 'none', metrics: {}) ⇒ Object

[View source]

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/middleware/metrics_middleware.rb', line 101

def process_prometheus_metric(type: 'none', metrics: {})
  return if metrics.blank?

  prometheus = Prometheus::Client.registry
  most_recent_aggregation = {}
  if Prometheus::Client.config.data_store.is_a?(Prometheus::Client::DataStores::DirectFileStore)
    most_recent_aggregation[:aggregation] = :most_recent
  end
  metrics.each do |key, value|
    next if %w[app_name url].include?(key.to_s)

    if value.is_a?(Hash)
      process_prometheus_metric(type: key.to_s, metrics: value)
    else
      gauge_name = key.to_s.downcase.gsub(/[^a-z0-9_]/, '_')
      gauge = prometheus.get(gauge_name.to_sym) || prometheus.gauge(
        gauge_name.to_sym,
        docstring: "#{key} metric",
        labels: %i(type name),
        preset_labels: { type: type, name: ZuoraObservability::Env.app_name },
        store_settings: most_recent_aggregation
      )
      gauge.set(value)
    end
  end
end