class JSONAPI::Rack::QueryTracking
def add_jpie_headers(headers)
def add_jpie_headers(headers) tracking = Thread.current[:jpie_query_tracking] return if tracking.nil? count = tracking[:count] headers["X-JPie-Query-Count"] = count.to_s warning_str = build_performance_warning_header(count) headers["X-JPie-Performance-Warning"] = warning_str if warning_str.present? end
def build_excessive_queries_payload(env, count, threshold, queries)
def build_excessive_queries_payload(env, count, threshold, queries) build_payload(env).merge( query_count: count, threshold: threshold, queries: queries, ) end
def build_payload(env)
def build_payload(env) base = env.nil? ? {} : request_payload_from_env(env) base.merge(correlation_id: JSONAPI::Support::CorrelationId.resolve) end
def build_performance_warning_header(count)
def build_performance_warning_header(count) warnings = (Thread.current[:jpie_warnings] || []).dup warnings << "excessive_queries" if count > JSONAPI.configuration.query_count_threshold warnings.uniq.join(",").presence end
def call(env)
def call(env) return @app.call(env) unless JSONAPI.configuration.query_tracking_enabled return @app.call(env) unless jsonapi_request?(env) call_with_tracking(env) ensure emit_excessive_queries_if_needed Thread.current[:jpie_query_tracking] = nil Thread.current[:jpie_warnings] = nil end
def call_with_tracking(env)
def call_with_tracking(env) Thread.current[:jpie_query_tracking] = { count: 0, queries: [], env: env } Thread.current[:jpie_warnings] = [] if JSONAPI.configuration.jpie_headers_enabled status, headers, body = @app.call(env) add_jpie_headers(headers) if JSONAPI.configuration.jpie_headers_enabled [status, headers, body] end
def emit_excessive_queries_if_needed
def emit_excessive_queries_if_needed tracking = Thread.current[:jpie_query_tracking] return if tracking.nil? count = tracking[:count] threshold = JSONAPI.configuration.query_count_threshold return if count <= threshold return unless defined?(Rails) && Rails.respond_to?(:event) notify_excessive_queries(tracking, count, threshold) end
def initialize(app)
def initialize(app) @app = app end
def jsonapi_request?(env)
def jsonapi_request?(env) env["HTTP_ACCEPT"].to_s.include?("application/vnd.api+json") end
def notify_excessive_queries(tracking, count, threshold)
def notify_excessive_queries(tracking, count, threshold) cap = JSONAPI.configuration.query_tracking_queries_cap queries = tracking[:queries].first(cap) payload = build_excessive_queries_payload(tracking[:env], count, threshold, queries) Rails.event.tagged("jsonapi", "excessive_queries") do Rails.event.notify("jpie.excessive_queries_detected", payload) end end
def request_payload_from_env(env)
def request_payload_from_env(env) req = ActionDispatch::Request.new(env) params = req.params { path: req.path, method: req.request_method, include: params[:include], resource_type: params[:resource_type], resource_id: params[:id], } end