lib/middleware/metrics_middleware.rb



module Middleware
  require 'uri'

  # Object of this class is passed to the ActiveSupport::Notification hook
  class PageRequest

    # This method is triggered when a non error page is loaded (not 404)
    def call(name, started, finished, unique_id, payload)
      if ZuoraConnect.configuration.enable_inbound_metrics_flag == true
        # If the url contains any css or JavaScript files then do not collect metrics for them
        return nil if ["css", "assets", "jpg", "png", "jpeg", "ico"].any? { |word| payload[:path].include?(word) }

        # Getting the endpoint and the content_type
        content_hash = {:html => "text/html", :js => "application/javascript", :json => "application/json"}
        content_type = content_hash.key?(payload[:format]) ? content_hash[payload[:format]] : payload[:format]

        # payloads with 500 requests do not have status as it is not set by the controller
        # https://github.com/rails/rails/issues/33335
        #status_code = payload[:status] ? payload[:status] : payload[:exception_object].present? ? 500 : ""
        if payload[:exception].present? 
          status_code, exception = [500, payload[:exception].first]
        else
          status_code, exception = [payload[:status], nil]
        end

        tags = {method: payload[:method], status: status_code, error_type: exception, content_type: content_type, controller: payload[:controller], action: payload[:action]}.compact

        values = {view_time: payload[:view_runtime], db_time: payload[:db_runtime], response_time: ((finished-started)*1000)}.compact
        values = values.map{ |k,v| [k,v.round(2)]}.to_h

        ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: :inbound, tags: tags, values: values)
      end
    end
  end

  class MetricsMiddleware

    require "zuora_connect/version"
    require "zuora_api/version"

    def initialize(app)
      @app = app
    end

    def call(env)
      start_time = Time.now
      @status, @headers, @response = @app.call(env)

      if ZuoraConnect.configuration.enable_inbound_metrics_flag == true
        # If the url contains any CSS or JavaScript files then do not collect metrics for them
        return [@status, @headers, @response] if ["css", "assets", "jpg", "png", "jpeg", "ico"].any? { |word| env['PATH_INFO'].include?(word) }

        if defined? Prometheus
          #Prometheus Stuff
          if env['PATH_INFO'] == '/connect/internal/metrics'

            #Do something before each scrape
            if defined? Resque.redis

              app_name = ENV['DEIS_APP'].present? ? "#{ENV['DEIS_APP']}" : "#{Rails.application.class.parent_name}"
              begin

                Resque.redis.ping

                Prometheus::REDIS_CONNECTION.set({connection:'redis',name:app_name},1)
                Prometheus::FINISHED_JOBS.set({type:'resque',name:app_name},Resque.info[:processed])
                Prometheus::PENDING_JOBS.set({type:'resque',name:app_name},Resque.info[:pending])
                Prometheus::ACTIVE_WORKERS.set({type:'resque',name:app_name},Resque.info[:working])
                Prometheus::WORKERS.set({type:'resque',name:app_name},Resque.info[:workers])
                Prometheus::FAILED_JOBS.set({type:'resque',name:app_name},Resque.info[:failed])

              rescue Redis::CannotConnectError
                  Prometheus::REDIS_CONNECTION.set({connection:'redis',name:app_name},0)
              end

              if ZuoraConnect.configuration.custom_prometheus_update_block != nil
                ZuoraConnect.configuration.custom_prometheus_update_block.call()
              end
            end

          end
        end

        # Uncomment following block of code for handling engine requests/requests without controller
        # else
        #   # Handling requests which do not have controllers (engines)
        if env["SCRIPT_NAME"].present?
          controller_path = "#{env['SCRIPT_NAME'][1..-1]}"
          controller_path = controller_path.sub("/", "::")
          request_path = "#{controller_path}#UnknownAction"
        else
          # Writing to telegraf: Handle 404
          if [404].include?(@status)
            content_type = @headers['Content-Type'].split(';')[0] if @headers['Content-Type']

            tags = {status: @status, content_type: content_type}
         
            tags = tags.merge({controller: 'ActionController', action: 'RoutingError' })
            
            values = {response_time: ((Time.now - start_time)*1000).round(2) }

            ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: :inbound, tags: tags, values: values)
          end
        end
      end
      [@status, @headers, @response]
    end
  end
end