Skip to content
Snippets Groups Projects
Commit 3f746d7d authored by Brett Walker's avatar Brett Walker
Browse files

Create the new AST LoggerAnalyzer

ported from the original LoggerAnalyzer
parent f7ec71a7
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !27536. Comments created here will be created in the context of that merge request.
...@@ -10,7 +10,7 @@ class GitlabSchema < GraphQL::Schema ...@@ -10,7 +10,7 @@ class GitlabSchema < GraphQL::Schema
DEFAULT_MAX_DEPTH = 15 DEFAULT_MAX_DEPTH = 15
AUTHENTICATED_MAX_DEPTH = 20 AUTHENTICATED_MAX_DEPTH = 20
if Feature.enabled?(:graphql_interpeter) if Feature.enabled?(:graphql_interpreter)
use GraphQL::Execution::Interpreter use GraphQL::Execution::Interpreter
# And, when you have updated your analyzers: # And, when you have updated your analyzers:
use GraphQL::Analysis::AST use GraphQL::Analysis::AST
...@@ -30,7 +30,7 @@ class GitlabSchema < GraphQL::Schema ...@@ -30,7 +30,7 @@ class GitlabSchema < GraphQL::Schema
use Gitlab::Graphql::Pagination::Connections use Gitlab::Graphql::Pagination::Connections
use Gitlab::Graphql::Timeout, max_seconds: Gitlab.config.gitlab.graphql_timeout use Gitlab::Graphql::Timeout, max_seconds: Gitlab.config.gitlab.graphql_timeout
if Feature.enabled?(:graphql_interpeter) if Feature.enabled?(:graphql_interpreter)
query_analyzer Gitlab::Graphql::QueryAnalyzers::AST::LoggerAnalyzer query_analyzer Gitlab::Graphql::QueryAnalyzers::AST::LoggerAnalyzer
query_analyzer Gitlab::Graphql::QueryAnalyzers::AST::RecursionAnalyzer query_analyzer Gitlab::Graphql::QueryAnalyzers::AST::RecursionAnalyzer
else else
......
---
name: graphql_interpreter
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27536
rollout_issue_url:
milestone: '13.11'
type: development
group: group::project management
default_enabled: false
...@@ -5,51 +5,58 @@ module Graphql ...@@ -5,51 +5,58 @@ module Graphql
module QueryAnalyzers module QueryAnalyzers
module AST module AST
class LoggerAnalyzer < GraphQL::Analysis::AST::Analyzer class LoggerAnalyzer < GraphQL::Analysis::AST::Analyzer
COMPLEXITY_ANALYZER = GraphQL::Analysis::QueryComplexity.new { |query, complexity_value| complexity_value } COMPLEXITY_ANALYZER = GraphQL::Analysis::AST::QueryComplexity
DEPTH_ANALYZER = GraphQL::Analysis::QueryDepth.new { |query, depth_value| depth_value } DEPTH_ANALYZER = GraphQL::Analysis::AST::QueryDepth
FIELD_USAGE_ANALYZER = GraphQL::Analysis::AST::FieldUsage
ALL_ANALYZERS = [COMPLEXITY_ANALYZER, DEPTH_ANALYZER, FIELD_USAGE_ANALYZER].freeze
def analyze? attr_reader :results
Feature.enabled?(:graphql_logging, default_enabled: true)
end def initialize(query)
super
def initial_value(query)
variables = process_variables(query.provided_variables) variables = process_variables(query.provided_variables)
default_initial_values(query).merge({ @results = default_initial_values(query).merge({
query_string: query.query_string, operation_name: query.operation_name,
variables: variables query_string: query.query_string,
}) variables: variables
rescue => e })
rescue StandardError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e) Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
default_initial_values(query) @results = default_initial_values(query_or_multiplex)
end
def call(memo, visit_type, irep_node)
memo
end end
def final_value(memo) def result
return if memo.nil? complexity, depth, field_usages = GraphQL::Analysis::AST.analyze_query(@subject, ALL_ANALYZERS, multiplex_analyzers: [])
analyzers = [COMPLEXITY_ANALYZER, DEPTH_ANALYZER] results[:depth] = depth
complexity, depth = GraphQL::Analysis.analyze_query(memo[:query], analyzers) results[:complexity] = complexity
# This duration is not the execution time of the
# query but the execution time of the analyzer.
results[:duration_s] = duration(results[:time_started]).round(1)
results[:used_fields] = field_usages[:used_fields]
results[:used_deprecated_fields] = field_usages[:used_deprecated_fields]
memo[:depth] = depth RequestStore.store[:graphql_logs] ||= []
memo[:complexity] = complexity RequestStore.store[:graphql_logs] << results
memo[:duration_s] = duration(memo[:time_started]).round(1) GraphqlLogger.info(results.except!(:time_started, :query))
GraphqlLogger.info(memo.except!(:time_started, :query)) results
rescue => e rescue StandardError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e) Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
end end
private private
def process_variables(variables) def process_variables(variables)
if variables.respond_to?(:to_s) filtered_variables = filter_sensitive_variables(variables)
variables.to_s filtered_variables.try(:to_s) || filtered_variables
else end
variables
end def filter_sensitive_variables(variables)
ActiveSupport::ParameterFilter
.new(::Rails.application.config.filter_parameters)
.filter(variables)
end end
def duration(time_started) def duration(time_started)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Graphql::QueryAnalyzers::AST::LoggerAnalyzer do
let(:query) { GraphQL::Query.new(GitlabSchema, document: document, context: {}, variables: { body: 'some note' }) }
let(:document) do
GraphQL.parse <<-GRAPHQL
mutation createNote($body: String!) {
createNote(input: {noteableId: "1", body: $body}) {
note {
id
}
}
}
GRAPHQL
end
describe '#result' do
let(:monotonic_time_before) { 42 }
let(:monotonic_time_after) { 500 }
let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
before do
RequestStore.store[:graphql_logs] = nil
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_before, monotonic_time_after)
end
it 'returns the complexity, depth, duration, etc' do
expect(Gitlab::GraphqlLogger).to receive(:info).and_return(true)
result = GraphQL::Analysis::AST.analyze_query(query, [described_class], multiplex_analyzers: [])
expect(result.first[:duration_s]).to eq monotonic_time_duration
expect(result.first[:variables]).to eq '{:body=>"[FILTERED]"}'
expect(result.first[:depth]).to eq 3
expect(result.first[:complexity]).to eq 3
expect(result.first[:used_fields]).to eq ['Note.id', 'CreateNotePayload.note', 'Mutation.createNote']
expect(RequestStore.store[:graphql_logs]).to eq result
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment