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 the url contains any css or JavaScript files then do not collect metrics for them
      block_words = ["css", "assets", "jpg", "png", "jpeg", "ico"]
      if block_words.any? { |word| payload[:path].include?(word) }
        return nil
      end

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

      status_code = payload[:status] ? payload[:status] : ""

      # 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 = 500 if payload[:exception_object].present?

      # Write to telegraf
      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")

    end
  end


  class MetricsMiddleware

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

    def initialize(app)
      @app = app
    end

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

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


      response_time = end_time - start_time

      #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

      # Writing to telegraf: Handle 404 and 500 requests
      if @status == 404 || @status == 304
        # Getting the endpoint and content_type
        request_path = @status == 404 ? "ActionController#RoutingError" : env["action_controller.instance"].present? ? "#{env["action_controller.instance"].class}##{env["action_controller.instance"].action_name}" : ""

        # 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"
        #   end

        content_type = @headers['Content-Type'].split(';')[0] if @headers['Content-Type']
        ZuoraConnect::AppInstanceBase.write_to_telegraf("endpoint_name": request_path, "method_name": env['REQUEST_METHOD'], "status_code": @status, "response_time": response_time,  "content_type": content_type, "direction": "inbound") if request_path.present?
      end

      [@status, @headers, @response]

    end

  end

end