Skip to content

Commit 66d4602

Browse files
author
Garth Kidd
committed
WIP: configurable span tracking behaviour
1 parent 96f2246 commit 66d4602

File tree

6 files changed

+176
-136
lines changed

6 files changed

+176
-136
lines changed

config/config.exs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ if Mix.env() == :test do
88
report_dir: "reports/exunit"
99

1010
config :opencensus,
11-
process_context: Opencensus.ProcessContext.DefaultImplementation,
11+
process_contexts: [
12+
Opencensus.Unstable.ProcessContext.SeqTrace,
13+
Opencensus.Unstable.ProcessContext.ProcessDictionary,
14+
Opencensus.Unstable.ProcessContext.ProcessDictionaryWithRecovery
15+
],
1216
reporters: [{Opencensus.TestSupport.SpanCaptureReporter, []}],
1317
send_interval_ms: 100
1418
end

lib/opencensus/process_context.ex

Lines changed: 0 additions & 63 deletions
This file was deleted.

lib/opencensus/trace.ex

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ defmodule Opencensus.Trace do
4646
```
4747
"""
4848

49-
@behaviour Opencensus.ProcessContext
50-
5149
defmacro with_child_span(label, attributes \\ quote(do: %{}), do: block) do
5250
line = __CALLER__.line
5351
module = __CALLER__.module
@@ -63,22 +61,23 @@ defmodule Opencensus.Trace do
6361
})
6462

6563
quote do
66-
previous_span_ctx = Opencensus.Trace.get_span_ctx()
67-
parent_span_ctx = Opencensus.Trace.effective_span_ctx()
64+
previous_span_ctx = Opencensus.Unstable.current_span_ctx()
65+
parent_span_ctx = Opencensus.Unstable.recover_span_ctx()
6866

6967
new_span_ctx =
7068
:oc_trace.start_span(unquote(label), parent_span_ctx, %{
7169
:attributes => unquote(computed_attributes)
7270
})
7371

74-
_ = Opencensus.Trace.put_span_ctx(new_span_ctx)
72+
_ = Opencensus.Unstable.with_span_ctx(new_span_ctx)
73+
^new_span_ctx = Opencensus.Unstable.current_span_ctx()
7574
Opencensus.Logger.set_logger_metadata()
7675

7776
try do
7877
unquote(block)
7978
after
8079
_ = :oc_trace.finish_span(new_span_ctx)
81-
_ = Opencensus.Trace.put_span_ctx(previous_span_ctx)
80+
_ = Opencensus.Unstable.with_span_ctx(previous_span_ctx)
8281
Opencensus.Logger.set_logger_metadata()
8382
end
8483
end
@@ -170,62 +169,4 @@ defmodule Opencensus.Trace do
170169
"""
171170
@spec await(Task.t(), :infinity | pos_integer()) :: term()
172171
defdelegate await(task, timeout \\ 5000), to: Task
173-
174-
@doc """
175-
Put the current span context.
176-
177-
Replaces `:ocp.with_span_ctx/1`.
178-
179-
Callers [MAY] pass values from `get_span_ctx/0` to `put_span_ctx/1`.
180-
181-
Callers [MUST NOT] pass a value obtained via `recover_span_ctx/0` to `put_span_ctx/1`.
182-
183-
Uses the configured `process_context`. See also: `Opencensus.ProcessContext`.
184-
185-
[MAY]: https://tools.ietf.org/html/rfc2119#section-5
186-
"""
187-
@impl Opencensus.ProcessContext
188-
def put_span_ctx(span_ctx), do: process_context() |> apply(:put_span_ctx, [span_ctx])
189-
190-
@doc """
191-
Get the current span context.
192-
193-
Replaces `:ocp.current_span_ctx/0`, along with `recover_span_ctx/0`.
194-
195-
Callers [MAY] pass values from `get_span_ctx/0` to `put_span_ctx/1`.
196-
197-
Uses the configured `process_context`. See also: `Opencensus.ProcessContext`.
198-
199-
[MAY]: https://tools.ietf.org/html/rfc2119#section-5
200-
"""
201-
@impl Opencensus.ProcessContext
202-
def get_span_ctx, do: process_context() |> apply(:get_span_ctx, [])
203-
204-
@doc """
205-
Recover the current span context by less reliable means.
206-
207-
Replaces `:ocp.current_span_ctx/0`, along with `get_span_ctx/0`.
208-
209-
Callers [MUST NOT] pass a value obtained via `recover_span_ctx/0` to `put_span_ctx/1`.
210-
211-
Uses the configured `process_context`. See also: `Opencensus.ProcessContext`.
212-
"""
213-
@impl Opencensus.ProcessContext
214-
def recover_span_ctx, do: process_context() |> apply(:recover_span_ctx, [])
215-
216-
@doc false
217-
def effective_span_ctx do
218-
case get_span_ctx() do
219-
:undefined -> recover_span_ctx()
220-
span_ctx -> span_ctx
221-
end
222-
end
223-
224-
defp process_context do
225-
Application.get_env(
226-
:opencensus,
227-
:process_context,
228-
Opencensus.ProcessContext.DefaultImplementation
229-
)
230-
end
231172
end

lib/opencensus/unstable.ex

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
defmodule Opencensus.Unstable do
2+
@moduledoc """
3+
Experimental higher-level API built on proposed `ot_ctx` behaviour.
4+
"""
5+
6+
@doc """
7+
Get the current span context.
8+
9+
Uses the first configured `process_context` only to ensure the value is safe to pass to
10+
`with_span_ctx/1` and `with_span_ctx/2` after you've finished your work.
11+
"""
12+
@spec current_span_ctx() :: :opencensus.span_ctx() | :undefined
13+
def current_span_ctx do
14+
process_contexts()
15+
|> hd
16+
|> get_span_ctx_via()
17+
end
18+
19+
@doc """
20+
Recovers the span context.
21+
22+
Uses all configured `process_context`.
23+
Results MAY be used as the parent of a new span.
24+
Results MUST NOT be passed to `with_span_ctx/1` or `with_span_ctx/2`.
25+
"""
26+
@spec recover_span_ctx() :: :opencensus.span_ctx() | :undefined
27+
def recover_span_ctx do
28+
process_contexts()
29+
|> Enum.find_value(:undefined, &get_span_ctx_via/1)
30+
end
31+
32+
@doc """
33+
Sets the span context. Replaces `:ocp.with_span_ctx/1`.
34+
35+
Uses all configured `process_context`.
36+
Returns the previous value of `current_span_ctx/0`.
37+
"""
38+
@spec with_span_ctx(span_ctx :: :opencensus.span_ctx() | :undefined) ::
39+
:opencensus.span_ctx() | :undefined
40+
def with_span_ctx(span_ctx) do
41+
return_span_ctx = current_span_ctx()
42+
process_contexts() |> Enum.each(&put_span_ctx_via(&1, span_ctx))
43+
return_span_ctx
44+
end
45+
46+
defp get_span_ctx_via(module) do
47+
apply(module, :get, [span_ctx_key()])
48+
|> case do
49+
nil -> :undefined
50+
span_ctx -> span_ctx
51+
end
52+
end
53+
54+
defp put_span_ctx_via(module, value) do
55+
apply(module, :with_value, [span_ctx_key(), value])
56+
end
57+
58+
@spec span_ctx_key() :: atom()
59+
defp span_ctx_key do
60+
Application.get_env(:opencensus, :span_ctx_key, :oc_span_ctx_key)
61+
end
62+
63+
@spec process_contexts() :: list(module())
64+
defp process_contexts do
65+
Application.get_env(:opencensus, :process_contexts, [
66+
Opencensus.Unstable.ProcessContext.SeqTrace,
67+
Opencensus.Unstable.ProcessContext.ProcessDictionary,
68+
Opencensus.Unstable.ProcessContext.ProcessDictionaryWithRecovery
69+
])
70+
end
71+
end
72+
73+
defmodule Opencensus.Unstable.ProcessContext do
74+
@moduledoc "Abstraction over process-local storage."
75+
76+
@doc "Get a value."
77+
@callback get(key :: atom()) :: any() | nil
78+
79+
@doc "Put a value."
80+
@callback with_value(key :: atom, value :: any()) :: :ok
81+
end
82+
83+
defmodule Opencensus.Unstable.ProcessContext.SeqTrace do
84+
@moduledoc """
85+
Process-local storage using `seq_trace`.
86+
87+
Shares well with any other use that maintains a namespace in the second element of a 2-tuple
88+
`{:shared_label, _map}`. Otherwise leaves the trace label alone to avoid disrupting the other
89+
usage.
90+
"""
91+
92+
@behaviour Opencensus.Unstable.ProcessContext
93+
94+
@doc "Get a value from the shared `seq_trace` label."
95+
@impl Opencensus.Unstable.ProcessContext
96+
def get(key) do
97+
case :seq_trace.get_token(:label) do
98+
{:label, {:shared_label, %{^key => value}}} ->
99+
value
100+
101+
_ ->
102+
nil
103+
end
104+
end
105+
106+
@doc "Put a value to the shared `seq_trace` label if safe."
107+
@impl Opencensus.Unstable.ProcessContext
108+
def with_value(key, value) do
109+
case :seq_trace.get_token(:label) do
110+
[] ->
111+
:seq_trace.set_token(:label, {:shared_label, %{key => value}})
112+
113+
{:label, {:shared_label, map}} when is_map(map) ->
114+
:seq_trace.set_token(:label, {:shared_label, Map.put(map, key, value)})
115+
116+
_ ->
117+
nil
118+
end
119+
120+
:ok
121+
end
122+
end
123+
124+
defmodule Opencensus.Unstable.ProcessContext.ProcessDictionary do
125+
@moduledoc """
126+
Process-local storage using the process dictionary.
127+
"""
128+
129+
@behaviour Opencensus.Unstable.ProcessContext
130+
131+
@doc "Get a value from the process dictionary."
132+
@impl Opencensus.Unstable.ProcessContext
133+
def get(key), do: Process.get(key)
134+
135+
@impl Opencensus.Unstable.ProcessContext
136+
def with_value(key, value) do
137+
Process.put(key, value)
138+
:ok
139+
end
140+
end
141+
142+
defmodule Opencensus.Unstable.ProcessContext.ProcessDictionaryWithRecovery do
143+
@moduledoc """
144+
Process-local storage using the process dictionary.
145+
"""
146+
147+
@behaviour Opencensus.Unstable.ProcessContext
148+
149+
@doc "Get a value from the process dictionary."
150+
@impl Opencensus.Unstable.ProcessContext
151+
def get(key) do
152+
[self() | Process.get(:"$callers", [])]
153+
|> Enum.find_value(fn pid -> pid |> Process.info() |> get_in([:dictionary, key]) end)
154+
end
155+
156+
@impl Opencensus.Unstable.ProcessContext
157+
defdelegate with_value(key, value), to: Opencensus.Unstable.ProcessContext.ProcessDictionary
158+
end

test/opencensus_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ defmodule OpencensusTest do
1111
on_exit(make_ref(), &detach/0)
1212

1313
assert Logger.metadata() == []
14-
assert :ocp.current_span_ctx() == :undefined
14+
assert Opencensus.Unstable.current_span_ctx() == :undefined
1515

1616
with_child_span "child_span" do
1717
:do_something
1818

19-
assert :ocp.current_span_ctx() != :undefined
19+
assert Opencensus.Unstable.current_span_ctx() != :undefined
2020

2121
assert Logger.metadata() |> Keyword.keys() |> Enum.sort() == [
2222
:span_id,

test/opencensus_trace_async_test.exs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ defmodule Opencensus.AsyncTest do
77
alias Opencensus.Trace
88

99
test "Trace.async/1" do
10-
assert :ocp.current_span_ctx() == :undefined
10+
assert Opencensus.Unstable.current_span_ctx() == :undefined
1111

1212
{inner, outer} =
1313
Trace.with_child_span "outside" do
14-
outer = :ocp.current_span_ctx() |> Span.load()
14+
outer = Opencensus.Unstable.current_span_ctx() |> Span.load()
1515

1616
Trace.async(fn ->
1717
Trace.with_child_span "inside" do
18-
inner = :ocp.current_span_ctx() |> Span.load()
18+
inner = Opencensus.Unstable.current_span_ctx() |> Span.load()
1919
{inner, outer}
2020
end
2121
end)
@@ -30,18 +30,18 @@ defmodule Opencensus.AsyncTest do
3030
defmodule M do
3131
def f(outer) do
3232
Trace.with_child_span "inside" do
33-
inner = :ocp.current_span_ctx() |> Span.load()
33+
inner = Opencensus.Unstable.current_span_ctx() |> Span.load()
3434
{inner, outer}
3535
end
3636
end
3737
end
3838

3939
test "Trace.async/3" do
40-
assert :ocp.current_span_ctx() == :undefined
40+
assert Opencensus.Unstable.current_span_ctx() == :undefined
4141

4242
{inner, outer} =
4343
Trace.with_child_span "outside" do
44-
outer = :ocp.current_span_ctx() |> Span.load()
44+
outer = Opencensus.Unstable.current_span_ctx() |> Span.load()
4545
M |> Trace.async(:f, [outer]) |> Trace.await(10)
4646
end
4747

0 commit comments

Comments
 (0)