Commit 03b07391 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Log durations for failing GraphQL-queries

If the query were to break by a Runtime error, even if it's handled
later on in the case of an `GraphQL::Error` subclass.

This makes sure that we still include a duration for those requests in
the logs.

This also adds information about the exception to the logs.
parent 2cccf3de
......@@ -11,19 +11,20 @@ module Gitlab
end
def trace(key, data)
result = yield
yield
rescue StandardError => e
data[:exception] = e
raise e
ensure
case key
when "execute_query"
log_execute_query(**data)
end
result
end
private
def log_execute_query(query: nil, duration_s: 0)
def log_execute_query(query: nil, duration_s: 0, exception: nil)
# execute_query should always have :query, but we're just being defensive
return unless query
......@@ -39,6 +40,8 @@ module Gitlab
query_string: query.query_string
}
Gitlab::ExceptionLogFormatter.format!(exception, info)
info.merge!(::Gitlab::ApplicationContext.current)
info.merge!(analysis_info) if analysis_info
......
......@@ -17,13 +17,9 @@ module Gitlab
def trace(key, data)
start_time = Gitlab::Metrics::System.monotonic_time
result = yield
duration_s = Gitlab::Metrics::System.monotonic_time - start_time
data[:duration_s] = duration_s
result
yield
ensure
data[:duration_s] = Gitlab::Metrics::System.monotonic_time - start_time
end
end
end
......
# frozen_string_literal: true
require "fast_spec_helper"
require "spec_helper"
require "support/graphql/fake_query_type"
RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do
......@@ -49,4 +49,15 @@ RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do
dummy_schema.execute(query_string, variables: variables)
end
it 'logs exceptions for breaking queries' do
query_string = "query fooOperation { breakingField }"
expect(::Gitlab::GraphqlLogger).to receive(:info).with(a_hash_including({
'exception.message' => 'This field is supposed to break',
'exception.class' => 'RuntimeError'
}))
expect { dummy_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
end
end
......@@ -20,6 +20,7 @@ RSpec.describe Gitlab::Graphql::Tracers::TimerTracer do
before do
current_time = 0
allow(tracer_spy).to receive(:trace)
allow(Gitlab::Metrics::System).to receive(:monotonic_time) do
current_time += expected_duration
end
......@@ -30,6 +31,18 @@ RSpec.describe Gitlab::Graphql::Tracers::TimerTracer do
dummy_schema.execute(query_string)
expect_to_have_traced(tracer_spy, expected_duration, query_string)
end
it "adds a duration_s even if the query failed" do
query_string = "query fooOperation { breakingField }"
expect { dummy_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
expect_to_have_traced(tracer_spy, expected_duration, query_string)
end
def expect_to_have_traced(tracer_spy, expected_duration, query_string)
# "parse" and "execute_query" are just arbitrary trace events
expect(tracer_spy).to have_received(:trace).with("parse", {
duration_s: expected_duration,
......
# frozen_string_literal: true
require 'graphql'
module Graphql
class FakeQueryType < Types::BaseObject
class FakeQueryType < ::GraphQL::Schema::Object
graphql_name 'FakeQuery'
field :hello_world, String, null: true do
argument :message, String, required: false
end
field :breaking_field, String, null: true
def hello_world(message: "world")
"Hello #{message}!"
end
def breaking_field
raise "This field is supposed to break"
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment