diff --git a/config/config.exs b/config/config.exs index 049386f..369af7c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -9,5 +9,5 @@ if Mix.env() == :test do config :opencensus, reporters: [{Opencensus.TestSupport.SpanCaptureReporter, []}], - send_interval_ms: 0 + send_interval_ms: 100 end diff --git a/lib/opencensus/span.ex b/lib/opencensus/span.ex index 6860fad..32ef62a 100644 --- a/lib/opencensus/span.ex +++ b/lib/opencensus/span.ex @@ -34,4 +34,10 @@ defmodule Opencensus.Span do def load(span_ctx) when is_tuple(span_ctx) do span_ctx |> SpanContext.from() |> Map.get(:span_id) |> load() end + + @doc false + def began_monotonic(record) when Record.is_record(record, :span) do + {native_time, _native_offset} = span(record, :start_time) + native_time + end end diff --git a/lib/opencensus/test_support/span_capture_reporter.ex b/lib/opencensus/test_support/span_capture_reporter.ex index a39a57f..6eabb7f 100644 --- a/lib/opencensus/test_support/span_capture_reporter.ex +++ b/lib/opencensus/test_support/span_capture_reporter.ex @@ -8,7 +8,7 @@ defmodule Opencensus.TestSupport.SpanCaptureReporter do ```elixir if Mix.env() == :test do config :opencensus, - send_interval_ms: 1, + send_interval_ms: 100, reporters: [{Opencensus.TestSupport.SpanCaptureReporter, []}] end ``` @@ -52,25 +52,52 @@ defmodule Opencensus.TestSupport.SpanCaptureReporter do end @doc false - def handler([__MODULE__], %{}, %{spans: spans}, pid), do: send(pid, {:spans, spans}) + def handler([__MODULE__], %{}, %{spans: spans}, {pid, attached_monotonic}) do + started_after_attach? = fn span -> span |> Span.began_monotonic() >= attached_monotonic end + filtered = spans |> Enum.filter(started_after_attach?) + send(pid, {:spans, filtered}) + end @doc "Attach the reporter to deliver spans to your process inbox." - def attach, do: :telemetry.attach(__MODULE__, [__MODULE__], &handler/4, self()) + def attach do + :telemetry.attach(__MODULE__, [__MODULE__], &handler/4, {self(), :erlang.monotonic_time()}) + end + + @doc """ + Detach the reporter to stop delivering spans to your process inbox. + + If still attached, triggers span delivery before detaching. + """ + def detach do + if handler_attached?(), do: trigger_and_wait_for_span_delivery() + :telemetry.detach(__MODULE__) + end - @doc "Detach the reporter to stop delivering spans to your process inbox." - def detach, do: :telemetry.detach(__MODULE__) + @doc """ + Collect spans from your process inbox. - @doc "Collect spans from your process inbox." + If still attached, triggers span delivery before collection. + """ @spec collect() :: list(%Span{}) def collect do + if handler_attached?(), do: trigger_and_wait_for_span_delivery() collect_span_records([]) |> Enum.map(&Span.from/1) end + defp trigger_and_wait_for_span_delivery do + send(:oc_reporter, :report_spans) + :timer.sleep(1) + end + + defp handler_attached? do + :telemetry.list_handlers([__MODULE__]) != [] + end + defp collect_span_records(acc) when is_list(acc) do receive do {:spans, spans} -> collect_span_records(acc ++ spans) after - 10 -> acc + 1 -> acc end end end diff --git a/test/opencensus_test_support_span_capture_reporter_test.exs b/test/opencensus_test_support_span_capture_reporter_test.exs new file mode 100644 index 0000000..e01d55c --- /dev/null +++ b/test/opencensus_test_support_span_capture_reporter_test.exs @@ -0,0 +1,43 @@ +defmodule Opencensus.TestSupport.SpanCaptureReporterTest do + use ExUnit.Case, async: false + import Opencensus.Trace + + alias Opencensus.TestSupport.SpanCaptureReporter + + describe "Opencensus.TestSupport.SpanCaptureReporter.collect/3" do + defp loop_count do + case System.get_env("CI") do + nil -> 10 + _ -> 1000 + end + end + + test "before detach, gets the just-finished span" do + for _ <- 1..loop_count() do + SpanCaptureReporter.attach() + + with_child_span "inner" do + [0, 1, 1, 2, 2, 2, 2, 3, 3, 4] |> Enum.random() |> :timer.sleep() + :... + end + + assert SpanCaptureReporter.collect() |> length() == 1 + SpanCaptureReporter.detach() + end + end + + test "after detach, gets the just-finished span" do + for _ <- 1..loop_count() do + SpanCaptureReporter.attach() + + with_child_span "inner" do + [0, 1, 1, 2, 2, 2, 2, 3, 3, 4] |> Enum.random() |> :timer.sleep() + :... + end + + SpanCaptureReporter.detach() + assert SpanCaptureReporter.collect() |> length() == 1 + end + end + end +end