Skip to content

Commit

Permalink
[COOP-566]: Add Measurement System for SLIs on Stonehenge Mutations/Q…
Browse files Browse the repository at this point in the history
…ueries (#134)

* [COOP-566]: Expose a telemetry event for handled graphql requests

* Update lib/opentelemetry_absinthe.ex

Co-authored-by: Cristiano Piemontese <[email protected]>

* Update change log and version number

* Add the schema as part of the payload of the telemetry event

---------

Co-authored-by: Cristiano Piemontese <[email protected]>
  • Loading branch information
Johnabell and cpiemontese authored Jan 12, 2024
1 parent ba4a5fa commit a62c8cc
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 34 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.1.0] - 2024-01-12

### Changed

- dispatch telemetry events for the handling of graphql requests

## [2.0.1] - 2023-03-14

### Changed
Expand Down
80 changes: 47 additions & 33 deletions lib/instrumentation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ defmodule OpentelemetryAbsinthe.Instrumentation do
require Logger
require Record

@telemetry [:opentelemetry_absinthe, :graphql, :handled]

@type graphql_handled_event_metadata :: %{
operation_name: String.t() | nil,
operation_type: :query | :mutation,
schema: Absinthe.Schema.t(),
status: :ok | :error
}

@type graphql_handled_event_measurements :: %{
duration: :int
}

@span_ctx_fields Record.extract(:span_ctx,
from_lib: "opentelemetry_api/include/opentelemetry.hrl"
)
Expand Down Expand Up @@ -88,41 +101,40 @@ defmodule OpentelemetryAbsinthe.Instrumentation do
Tracer.set_current_span(new_ctx)
end

def handle_operation_stop(_event_name, _measurements, data, config) do
def handle_operation_stop(_event_name, measurements, data, config) do
operation_type = get_operation_type(data)
operation_name = get_operation_name(data)

span_name = span_name(operation_type, operation_name, config.span_name)
Tracer.update_name(span_name)
operation_type
|> span_name(operation_name, config.span_name)
|> Tracer.update_name()

errors = data.blueprint.result[:errors]
status = status(errors)
set_status(status)

[]
|> put_if(config.trace_request_type, {@graphql_operation_type, operation_type})
|> put_if(config.trace_request_name, {@graphql_operation_name, operation_name})
|> put_if(config.trace_response_result, {:"graphql.response.result", Jason.encode!(data.blueprint.result)})
|> put_if(config.trace_response_errors, {:"graphql.response.errors", Jason.encode!(errors)})
|> put_if(
config.trace_request_selections,
fn -> {:"graphql.request.selections", data |> get_graphql_selections() |> Jason.encode!()} end
)
|> Tracer.set_attributes()

:telemetry.execute(
@telemetry,
measurements,
%{
operation_name: operation_name,
operation_type: operation_type,
schema: data.blueprint.schema,
status: status
}
)

result_attributes =
[]
|> put_if(
config.trace_request_type,
{@graphql_operation_type, operation_type}
)
|> put_if(
config.trace_request_name,
{@graphql_operation_name, operation_name}
)
|> put_if(
config.trace_response_result,
{:"graphql.response.result", Jason.encode!(data.blueprint.result)}
)
|> put_if(
config.trace_response_errors,
{:"graphql.response.errors", Jason.encode!(errors)}
)
|> put_if(
config.trace_request_selections,
fn -> {:"graphql.request.selections", data |> get_graphql_selections() |> Jason.encode!()} end
)

set_status(errors)

Tracer.set_attributes(result_attributes)
Tracer.end_span()

restore_parent_ctx()
Expand Down Expand Up @@ -176,8 +188,10 @@ defmodule OpentelemetryAbsinthe.Instrumentation do
Tracer.set_current_span(ctx)
end

# set status as `:error` in case of errors in the graphql response
defp set_status(nil), do: :ok
defp set_status([]), do: :ok
defp set_status(_errors), do: Tracer.set_status(OpenTelemetry.status(:error, ""))
defp status(nil), do: :ok
defp status([]), do: :ok
defp status(_error), do: :error

defp set_status(:ok), do: :ok
defp set_status(:error), do: Tracer.set_status(OpenTelemetry.status(:error, ""))
end
7 changes: 7 additions & 0 deletions lib/opentelemetry_absinthe.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ defmodule OpentelemetryAbsinthe do
* `trace_response_result`(default: #{Keyword.fetch!(@config, :trace_response_result)}): attaches the result returned by the server as an attribute
* `trace_response_errors`(default: #{Keyword.fetch!(@config, :trace_response_errors)}): attaches the errors returned by the server as an attribute
## Telemetry
OpentelemetryAbsinthe exposes `telemetry` events which can be hooked into using `:telemetry.attach/4` or `:telemetry.attach_many/4`.
The events exposed are:
- `[:opentelemetry_absinthe, :graphql, :handled]` for when a GraphQl query has been handled, the metadata and measurements are defined in `OpentelemetryAbsinthe.Instrumentation.graphql_handled_event_metadata()` and `OpentelemetryAbsinthe.Instrumentation.graphql_handled_event_measurements()`
"""

defdelegate setup(instrumentation_opts \\ []), to: Instrumentation
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule OpentelemetryAbsinthe.MixProject do
use Mix.Project

@source_url "https://github.com/primait/opentelemetry_absinthe"
@version "2.0.1-rc.0"
@version "2.1.0"

def project do
[
Expand Down

0 comments on commit a62c8cc

Please sign in to comment.