Skip to content

Commit

Permalink
WIP: configurable span tracking behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
Garth Kidd committed Aug 6, 2019
1 parent 96f2246 commit 66d4602
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 136 deletions.
6 changes: 5 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ if Mix.env() == :test do
report_dir: "reports/exunit"

config :opencensus,
process_context: Opencensus.ProcessContext.DefaultImplementation,
process_contexts: [
Opencensus.Unstable.ProcessContext.SeqTrace,
Opencensus.Unstable.ProcessContext.ProcessDictionary,
Opencensus.Unstable.ProcessContext.ProcessDictionaryWithRecovery
],
reporters: [{Opencensus.TestSupport.SpanCaptureReporter, []}],
send_interval_ms: 100
end
63 changes: 0 additions & 63 deletions lib/opencensus/process_context.ex

This file was deleted.

69 changes: 5 additions & 64 deletions lib/opencensus/trace.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ defmodule Opencensus.Trace do
```
"""

@behaviour Opencensus.ProcessContext

defmacro with_child_span(label, attributes \\ quote(do: %{}), do: block) do
line = __CALLER__.line
module = __CALLER__.module
Expand All @@ -63,22 +61,23 @@ defmodule Opencensus.Trace do
})

quote do
previous_span_ctx = Opencensus.Trace.get_span_ctx()
parent_span_ctx = Opencensus.Trace.effective_span_ctx()
previous_span_ctx = Opencensus.Unstable.current_span_ctx()
parent_span_ctx = Opencensus.Unstable.recover_span_ctx()

new_span_ctx =
:oc_trace.start_span(unquote(label), parent_span_ctx, %{
:attributes => unquote(computed_attributes)
})

_ = Opencensus.Trace.put_span_ctx(new_span_ctx)
_ = Opencensus.Unstable.with_span_ctx(new_span_ctx)
^new_span_ctx = Opencensus.Unstable.current_span_ctx()
Opencensus.Logger.set_logger_metadata()

try do
unquote(block)
after
_ = :oc_trace.finish_span(new_span_ctx)
_ = Opencensus.Trace.put_span_ctx(previous_span_ctx)
_ = Opencensus.Unstable.with_span_ctx(previous_span_ctx)
Opencensus.Logger.set_logger_metadata()
end
end
Expand Down Expand Up @@ -170,62 +169,4 @@ defmodule Opencensus.Trace do
"""
@spec await(Task.t(), :infinity | pos_integer()) :: term()
defdelegate await(task, timeout \\ 5000), to: Task

@doc """
Put the current span context.
Replaces `:ocp.with_span_ctx/1`.
Callers [MAY] pass values from `get_span_ctx/0` to `put_span_ctx/1`.
Callers [MUST NOT] pass a value obtained via `recover_span_ctx/0` to `put_span_ctx/1`.
Uses the configured `process_context`. See also: `Opencensus.ProcessContext`.
[MAY]: https://tools.ietf.org/html/rfc2119#section-5
"""
@impl Opencensus.ProcessContext
def put_span_ctx(span_ctx), do: process_context() |> apply(:put_span_ctx, [span_ctx])

@doc """
Get the current span context.
Replaces `:ocp.current_span_ctx/0`, along with `recover_span_ctx/0`.
Callers [MAY] pass values from `get_span_ctx/0` to `put_span_ctx/1`.
Uses the configured `process_context`. See also: `Opencensus.ProcessContext`.
[MAY]: https://tools.ietf.org/html/rfc2119#section-5
"""
@impl Opencensus.ProcessContext
def get_span_ctx, do: process_context() |> apply(:get_span_ctx, [])

@doc """
Recover the current span context by less reliable means.
Replaces `:ocp.current_span_ctx/0`, along with `get_span_ctx/0`.
Callers [MUST NOT] pass a value obtained via `recover_span_ctx/0` to `put_span_ctx/1`.
Uses the configured `process_context`. See also: `Opencensus.ProcessContext`.
"""
@impl Opencensus.ProcessContext
def recover_span_ctx, do: process_context() |> apply(:recover_span_ctx, [])

@doc false
def effective_span_ctx do
case get_span_ctx() do
:undefined -> recover_span_ctx()
span_ctx -> span_ctx
end
end

defp process_context do
Application.get_env(
:opencensus,
:process_context,
Opencensus.ProcessContext.DefaultImplementation
)
end
end
158 changes: 158 additions & 0 deletions lib/opencensus/unstable.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
defmodule Opencensus.Unstable do
@moduledoc """
Experimental higher-level API built on proposed `ot_ctx` behaviour.
"""

@doc """
Get the current span context.
Uses the first configured `process_context` only to ensure the value is safe to pass to
`with_span_ctx/1` and `with_span_ctx/2` after you've finished your work.
"""
@spec current_span_ctx() :: :opencensus.span_ctx() | :undefined
def current_span_ctx do
process_contexts()
|> hd
|> get_span_ctx_via()
end

@doc """
Recovers the span context.
Uses all configured `process_context`.
Results MAY be used as the parent of a new span.
Results MUST NOT be passed to `with_span_ctx/1` or `with_span_ctx/2`.
"""
@spec recover_span_ctx() :: :opencensus.span_ctx() | :undefined
def recover_span_ctx do
process_contexts()
|> Enum.find_value(:undefined, &get_span_ctx_via/1)
end

@doc """
Sets the span context. Replaces `:ocp.with_span_ctx/1`.
Uses all configured `process_context`.
Returns the previous value of `current_span_ctx/0`.
"""
@spec with_span_ctx(span_ctx :: :opencensus.span_ctx() | :undefined) ::
:opencensus.span_ctx() | :undefined
def with_span_ctx(span_ctx) do
return_span_ctx = current_span_ctx()
process_contexts() |> Enum.each(&put_span_ctx_via(&1, span_ctx))
return_span_ctx
end

defp get_span_ctx_via(module) do
apply(module, :get, [span_ctx_key()])
|> case do
nil -> :undefined
span_ctx -> span_ctx
end
end

defp put_span_ctx_via(module, value) do
apply(module, :with_value, [span_ctx_key(), value])
end

@spec span_ctx_key() :: atom()
defp span_ctx_key do
Application.get_env(:opencensus, :span_ctx_key, :oc_span_ctx_key)
end

@spec process_contexts() :: list(module())
defp process_contexts do
Application.get_env(:opencensus, :process_contexts, [
Opencensus.Unstable.ProcessContext.SeqTrace,
Opencensus.Unstable.ProcessContext.ProcessDictionary,
Opencensus.Unstable.ProcessContext.ProcessDictionaryWithRecovery
])
end
end

defmodule Opencensus.Unstable.ProcessContext do
@moduledoc "Abstraction over process-local storage."

@doc "Get a value."
@callback get(key :: atom()) :: any() | nil

@doc "Put a value."
@callback with_value(key :: atom, value :: any()) :: :ok
end

defmodule Opencensus.Unstable.ProcessContext.SeqTrace do
@moduledoc """
Process-local storage using `seq_trace`.
Shares well with any other use that maintains a namespace in the second element of a 2-tuple
`{:shared_label, _map}`. Otherwise leaves the trace label alone to avoid disrupting the other
usage.
"""

@behaviour Opencensus.Unstable.ProcessContext

@doc "Get a value from the shared `seq_trace` label."
@impl Opencensus.Unstable.ProcessContext
def get(key) do
case :seq_trace.get_token(:label) do
{:label, {:shared_label, %{^key => value}}} ->
value

_ ->
nil
end
end

@doc "Put a value to the shared `seq_trace` label if safe."
@impl Opencensus.Unstable.ProcessContext
def with_value(key, value) do
case :seq_trace.get_token(:label) do
[] ->
:seq_trace.set_token(:label, {:shared_label, %{key => value}})

{:label, {:shared_label, map}} when is_map(map) ->
:seq_trace.set_token(:label, {:shared_label, Map.put(map, key, value)})

_ ->
nil
end

:ok
end
end

defmodule Opencensus.Unstable.ProcessContext.ProcessDictionary do
@moduledoc """
Process-local storage using the process dictionary.
"""

@behaviour Opencensus.Unstable.ProcessContext

@doc "Get a value from the process dictionary."
@impl Opencensus.Unstable.ProcessContext
def get(key), do: Process.get(key)

@impl Opencensus.Unstable.ProcessContext
def with_value(key, value) do
Process.put(key, value)
:ok
end
end

defmodule Opencensus.Unstable.ProcessContext.ProcessDictionaryWithRecovery do
@moduledoc """
Process-local storage using the process dictionary.
"""

@behaviour Opencensus.Unstable.ProcessContext

@doc "Get a value from the process dictionary."
@impl Opencensus.Unstable.ProcessContext
def get(key) do
[self() | Process.get(:"$callers", [])]
|> Enum.find_value(fn pid -> pid |> Process.info() |> get_in([:dictionary, key]) end)
end

@impl Opencensus.Unstable.ProcessContext
defdelegate with_value(key, value), to: Opencensus.Unstable.ProcessContext.ProcessDictionary
end
4 changes: 2 additions & 2 deletions test/opencensus_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ defmodule OpencensusTest do
on_exit(make_ref(), &detach/0)

assert Logger.metadata() == []
assert :ocp.current_span_ctx() == :undefined
assert Opencensus.Unstable.current_span_ctx() == :undefined

with_child_span "child_span" do
:do_something

assert :ocp.current_span_ctx() != :undefined
assert Opencensus.Unstable.current_span_ctx() != :undefined

assert Logger.metadata() |> Keyword.keys() |> Enum.sort() == [
:span_id,
Expand Down
12 changes: 6 additions & 6 deletions test/opencensus_trace_async_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ defmodule Opencensus.AsyncTest do
alias Opencensus.Trace

test "Trace.async/1" do
assert :ocp.current_span_ctx() == :undefined
assert Opencensus.Unstable.current_span_ctx() == :undefined

{inner, outer} =
Trace.with_child_span "outside" do
outer = :ocp.current_span_ctx() |> Span.load()
outer = Opencensus.Unstable.current_span_ctx() |> Span.load()

Trace.async(fn ->
Trace.with_child_span "inside" do
inner = :ocp.current_span_ctx() |> Span.load()
inner = Opencensus.Unstable.current_span_ctx() |> Span.load()
{inner, outer}
end
end)
Expand All @@ -30,18 +30,18 @@ defmodule Opencensus.AsyncTest do
defmodule M do
def f(outer) do
Trace.with_child_span "inside" do
inner = :ocp.current_span_ctx() |> Span.load()
inner = Opencensus.Unstable.current_span_ctx() |> Span.load()
{inner, outer}
end
end
end

test "Trace.async/3" do
assert :ocp.current_span_ctx() == :undefined
assert Opencensus.Unstable.current_span_ctx() == :undefined

{inner, outer} =
Trace.with_child_span "outside" do
outer = :ocp.current_span_ctx() |> Span.load()
outer = Opencensus.Unstable.current_span_ctx() |> Span.load()
M |> Trace.async(:f, [outer]) |> Trace.await(10)
end

Expand Down

0 comments on commit 66d4602

Please sign in to comment.