Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions lib/sentry/event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -502,32 +502,12 @@ defmodule Sentry.Event do

case current_trace_context() do
nil -> contexts
trace -> Map.put(contexts, :trace, trace)
{trace_id, span_id} -> Map.put(contexts, :trace, %{trace_id: trace_id, span_id: span_id})
end
end

if Sentry.OpenTelemetry.VersionChecker.tracing_compatible?() do
defp current_trace_context do
case :otel_tracer.current_span_ctx() do
:undefined ->
nil

span_ctx ->
trace_id = :otel_span.trace_id(span_ctx)
span_id = :otel_span.span_id(span_ctx)

if trace_id != 0 and span_id != 0 do
%{trace_id: format_id(trace_id, 32), span_id: format_id(span_id, 16)}
end
end
end

defp format_id(id, length) do
id
|> Integer.to_string(16)
|> String.pad_leading(length, "0")
|> String.downcase()
end
defp current_trace_context, do: Sentry.OpenTelemetry.TraceContext.current()
else
defp current_trace_context, do: nil
end
Expand Down
44 changes: 6 additions & 38 deletions lib/sentry/metrics.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ defmodule Sentry.Metrics do
"""
@moduledoc since: "13.0.0"

alias Sentry.{ClientReport, Config, LoggerUtils, Metric, TelemetryProcessor}
alias Sentry.{ClientReport, Config, Metric, TelemetryProcessor}

@doc """
Records a counter metric.
Expand Down Expand Up @@ -117,8 +117,7 @@ defmodule Sentry.Metrics do
unit = Keyword.get(opts, :unit)
attributes = Keyword.get(opts, :attributes, %{})

{trace_id, span_id} = extract_trace_context()
trace_id = trace_id || generate_trace_id()
{trace_id, span_id} = current_trace_context() || {generate_trace_id(), nil}

# Build metric struct
metric = %Metric{
Expand Down Expand Up @@ -146,41 +145,10 @@ defmodule Sentry.Metrics do
:ok
end

defp extract_trace_context do
case :otel_tracer.current_span_ctx() do
:undefined ->
{nil, nil}

span_ctx ->
trace_id = :otel_span.trace_id(span_ctx)
span_id = :otel_span.span_id(span_ctx)

if trace_id != 0 and span_id != 0 do
{format_trace_id(trace_id), format_span_id(span_id)}
else
{nil, nil}
end
end
rescue
e in [UndefinedFunctionError, ArgumentError] ->
LoggerUtils.debug("Failed to extract OpenTelemetry trace context: #{inspect(e)}")
{nil, nil}
end

# Format trace_id as 32-character hex string
defp format_trace_id(trace_id) when is_integer(trace_id) do
trace_id
|> Integer.to_string(16)
|> String.pad_leading(32, "0")
|> String.downcase()
end

# Format span_id as 16-character hex string
defp format_span_id(span_id) when is_integer(span_id) do
span_id
|> Integer.to_string(16)
|> String.pad_leading(16, "0")
|> String.downcase()
if Sentry.OpenTelemetry.VersionChecker.tracing_compatible?() do
defp current_trace_context, do: Sentry.OpenTelemetry.TraceContext.current()
else
defp current_trace_context, do: nil
end

# Generate a random trace_id as fallback when no active span exists
Expand Down
30 changes: 30 additions & 0 deletions lib/sentry/opentelemetry/trace_context.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
if Sentry.OpenTelemetry.VersionChecker.tracing_compatible?() do
defmodule Sentry.OpenTelemetry.TraceContext do
@moduledoc false

@type t() :: {trace_id :: String.t(), span_id :: String.t()}

@spec current() :: t() | nil
def current do
case :otel_tracer.current_span_ctx() do
:undefined ->
nil

span_ctx ->
trace_id = :otel_span.trace_id(span_ctx)
span_id = :otel_span.span_id(span_ctx)

if trace_id != 0 and span_id != 0 do
{format_id(trace_id, 32), format_id(span_id, 16)}
end
end
Comment thread
solnic marked this conversation as resolved.
end

defp format_id(id, length) do
id
|> Integer.to_string(16)
|> String.pad_leading(length, "0")
|> String.downcase()
end
end
end
20 changes: 20 additions & 0 deletions test/sentry/metrics_integration_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule Sentry.MetricsIntegrationTest do
use Sentry.Case, async: false

require OpenTelemetry.Tracer, as: Tracer

import Sentry.TestHelpers
import Sentry.Test.Assertions

Expand Down Expand Up @@ -128,4 +130,22 @@ defmodule Sentry.MetricsIntegrationTest do
assert_sentry_metric(:distribution, name: "distribution.metric")
end
end

describe "trace context on recorded metrics" do
test "records the metric with the trace of the surrounding span", ctx do
put_test_config(traces_sample_rate: 1.0)

Tracer.with_span "checkout" do
Metrics.count("orders.placed", 1)
end

envelopes = collect_envelopes(ctx.ref, 2)

assert [%{"items" => [metric]}] = extract_metric_items(envelopes)
assert [transaction] = extract_transactions(envelopes)

assert metric["trace_id"] == transaction["contexts"]["trace"]["trace_id"]
assert metric["span_id"] == transaction["contexts"]["trace"]["span_id"]
end
end
end
2 changes: 2 additions & 0 deletions test/sentry/opentelemetry/integration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ defmodule Sentry.OpenTelemetry.IntegrationTest do
assert Code.ensure_loaded?(Sentry.OpenTelemetry.SpanProcessor)
assert Code.ensure_loaded?(Sentry.OpenTelemetry.Sampler)
assert Code.ensure_loaded?(Sentry.OpenTelemetry.SpanRecord)
assert Code.ensure_loaded?(Sentry.OpenTelemetry.TraceContext)
assert Code.ensure_loaded?(Sentry.OpenTelemetry.SpanStorage)

false ->
# When versions are incompatible, modules should not be defined
refute Code.ensure_loaded?(Sentry.OpenTelemetry.SpanProcessor)
refute Code.ensure_loaded?(Sentry.OpenTelemetry.Sampler)
refute Code.ensure_loaded?(Sentry.OpenTelemetry.SpanRecord)
refute Code.ensure_loaded?(Sentry.OpenTelemetry.TraceContext)
# SpanStorage should always be defined as it doesn't depend on OpenTelemetry directly
assert Code.ensure_loaded?(Sentry.OpenTelemetry.SpanStorage)
end
Expand Down
Loading