Skip to content

Commit

Permalink
Support custom metadata tags (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-hobbs-prima authored May 27, 2024
1 parent 189fe09 commit d13b509
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 14 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [2.3.1] - 2024-05-24

### Added

- Support custom metadata from integrators. Use `OpentelemetryAbsinthe.TelemetryMetadata` to add metadata to your context which will then be broadcast.

## [2.3.0-rc.0] - 2024-04-18

### Added
Expand Down
29 changes: 17 additions & 12 deletions lib/instrumentation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ defmodule OpentelemetryAbsinthe.Instrumentation do
code, it just won't do anything.)
"""
alias Absinthe.Blueprint
alias OpentelemetryAbsinthe.TelemetryMetadata

require OpenTelemetry.Tracer, as: Tracer
require OpenTelemetry.SemanticConventions.Trace, as: Conventions
require Logger
require Record

@type graphql_handled_event_metadata :: %{
operation_name: String.t() | nil,
operation_type: :query | :mutation,
schema: Absinthe.Schema.t(),
errors: [graphql_handled_event_error()] | nil,
status: :ok | :error
required(:operation_name) => String.t() | nil,
required(:operation_type) => :query | :mutation,
required(:schema) => Absinthe.Schema.t(),
required(:errors) => [graphql_handled_event_error()] | nil,
required(:status) => :ok | :error,
optional(atom()) => any()
}

@type graphql_handled_event_error :: %{
Expand Down Expand Up @@ -153,13 +155,16 @@ defmodule OpentelemetryAbsinthe.Instrumentation do
telemetry_provider().execute(
[:opentelemetry_absinthe, :graphql, :handled],
measurements,
%{
operation_name: operation_name,
operation_type: operation_type,
schema: data.blueprint.schema,
errors: errors,
status: status
}
Map.merge(
%{
operation_name: operation_name,
operation_type: operation_type,
schema: data.blueprint.schema,
errors: errors,
status: status
},
TelemetryMetadata.from_context(data.blueprint.execution.context)
)
)

Tracer.end_span()
Expand Down
20 changes: 20 additions & 0 deletions lib/telemetry_metadata.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule OpentelemetryAbsinthe.TelemetryMetadata do
@moduledoc """
A helper module to allow integrators to add custom data to their context
which will then be added to the [:opentelemetry_absinthe, :graphql, :handled]
event
"""
@key __MODULE__

@type absinthe_context :: map()
@type telemetry_metadata :: %{
optional(atom()) => any()
}

@spec update_context(absinthe_context(), telemetry_metadata()) :: absinthe_context()
def update_context(%{} = context, %{} = metadata),
do: Map.update(context, @key, metadata, &Map.merge(&1, metadata))

@spec from_context(absinthe_context()) :: telemetry_metadata()
def from_context(%{} = context), do: Map.get(context, @key, %{})
end
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.3.0-rc.0"
@version "2.3.1"

def project do
[
Expand Down
74 changes: 73 additions & 1 deletion test/instrumentation_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule OpentelemetryAbsintheTest.Instrumentation do
defmodule OpentelemetryAbsintheTest.InstrumentationTest do
use OpentelemetryAbsintheTest.Case

alias OpentelemetryAbsinthe.Instrumentation
alias OpentelemetryAbsinthe.TelemetryMetadata
alias OpentelemetryAbsintheTest.Support.GraphQL.Queries
alias OpentelemetryAbsintheTest.Support.Query

Expand Down Expand Up @@ -36,4 +38,74 @@ defmodule OpentelemetryAbsintheTest.Instrumentation do

assert @trace_attributes = attrs |> Map.keys() |> Enum.sort()
end

describe "handles metadata" do
setup do
config =
Instrumentation.default_config()
|> Keyword.put(:type, :operation)
|> Enum.into(%{})

%{config: config}
end

test "standard values are returned when no metadata in context", ctx do
assert :ok =
:telemetry.attach(
ctx.test,
[:opentelemetry_absinthe, :graphql, :handled],
fn _telemetry_event, _measurements, metadata, _config ->
send(self(), metadata)
end,
nil
)

assert :ok =
Instrumentation.handle_stop(
"Test",
%{},
%{blueprint: BlueprintArchitect.blueprint(schema: __MODULE__)},
ctx.config
)

assert_receive %{
operation_name: "TestOperation",
operation_type: :query,
schema: __MODULE__,
errors: nil,
status: :ok
},
10
end

test "standard values are returned alongside the metadata from context", ctx do
context = TelemetryMetadata.update_context(%{}, %{source: "TestSource", user_agent: "Insomnia"})

blueprint =
BlueprintArchitect.blueprint(schema: __MODULE__, execution: BlueprintArchitect.execution(context: context))

assert :ok =
:telemetry.attach(
ctx.test,
[:opentelemetry_absinthe, :graphql, :handled],
fn _telemetry_event, _measurements, metadata, _config ->
send(self(), metadata)
end,
nil
)

assert :ok = Instrumentation.handle_stop("Test", %{}, %{blueprint: blueprint}, ctx.config)

assert_receive %{
operation_name: "TestOperation",
operation_type: :query,
schema: __MODULE__,
errors: nil,
status: :ok,
user_agent: "Insomnia",
source: "TestSource"
},
10
end
end
end
32 changes: 32 additions & 0 deletions test/support/blueprint_architect.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule BlueprintArchitect do
@moduledoc false

alias Absinthe.Blueprint

@spec blueprint(keyword()) :: Blueprint.t()
def blueprint(overrides \\ []) do
%{
operations: [operation()]
}
|> Map.merge(Enum.into(overrides, %{}))
|> then(&struct!(Blueprint, &1))
end

@spec operation(keyword()) :: Blueprint.Document.Operation.t()
def operation(overrides \\ []) do
%{
name: "TestOperation",
type: :query,
current: true
}
|> Map.merge(Enum.into(overrides, %{}))
|> then(&struct!(Blueprint.Document.Operation, &1))
end

@spec execution(keyword()) :: Blueprint.Execution.t()
def execution(overrides \\ []) do
%{}
|> Map.merge(Enum.into(overrides, %{}))
|> then(&struct!(Blueprint.Execution, &1))
end
end
20 changes: 20 additions & 0 deletions test/telemetry_metadata_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule OpentelemetryAbsinthe.TelemetryMetadataTest do
use ExUnit.Case

alias OpentelemetryAbsinthe.TelemetryMetadata

test "should return an empty metadata when context is empty" do
assert %{} == TelemetryMetadata.from_context(%{})
end

test "should return an empty metadata when context does not contain metadata" do
assert %{} == TelemetryMetadata.from_context(%{foo: :bar})
end

test "should return same metadata that was stored" do
assert %{user_agent: :test} ==
%{}
|> TelemetryMetadata.update_context(%{user_agent: :test})
|> TelemetryMetadata.from_context()
end
end

0 comments on commit d13b509

Please sign in to comment.