-
Notifications
You must be signed in to change notification settings - Fork 13
separate context propagation otep #4
Changes from 5 commits
8c9670a
75d19ef
e248427
80b7dc9
9ad67e0
00d8ab9
e2c5384
2099f10
f0d91bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,8 +29,14 @@ | |
-module(opentelemetry). | ||
|
||
-export([set_default_tracer/1, | ||
set_default_context_manager/1, | ||
get_context_manager/0, | ||
get_tracer/0, | ||
get_tracer/1, | ||
set_http_extractor/1, | ||
get_http_extractor/0, | ||
set_http_injector/1, | ||
get_http_injector/0, | ||
timestamp/0, | ||
links/1, | ||
link/4, | ||
|
@@ -106,12 +112,42 @@ | |
set_default_tracer(Tracer) -> | ||
persistent_term:put({?MODULE, default_tracer}, Tracer). | ||
|
||
set_default_context_manager(ContextModule) -> | ||
persistent_term:put({?MODULE, context_manager}, ContextModule). | ||
|
||
get_context_manager() -> | ||
persistent_term:get({?MODULE, context_manager}, {ot_ctx_noop, []}). | ||
|
||
get_tracer() -> | ||
persistent_term:get({?MODULE, default_tracer}, {ot_tracer_noop, []}). | ||
|
||
get_tracer(_Name) -> | ||
persistent_term:get({?MODULE, default_tracer}, {ot_tracer_noop, []}). | ||
|
||
set_http_extractor(List) -> | ||
Fun = fun(Headers) -> | ||
lists:foldl(fun(Extract, ok) -> | ||
Extract(Headers), | ||
ok | ||
end, ok, List), | ||
ok | ||
end, | ||
persistent_term:put({?MODULE, http_extractor}, Fun). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This module is no longer safe for hot-code loading, since after two editions of this module being loaded, the Fun will need to be reaped, ending up killing all processes that hold a reference to it? Though from a quick test, it seems like it just eventually turns into a badfun, so it's not that bad? I guess the only workaround would be to extract the function to have a kind of "state" argument and use a fully qualified fun to make it work. Probably not worth the effort at this point in time? |
||
|
||
set_http_injector(List) -> | ||
Fun = fun(Headers) -> | ||
lists:foldl(fun(Inject, HeadersAcc) -> | ||
Inject(HeadersAcc) | ||
end, Headers, List) | ||
end, | ||
persistent_term:put({?MODULE, http_injector}, Fun). | ||
|
||
get_http_extractor() -> | ||
persistent_term:get({?MODULE, http_extractor}, fun(Headers) -> Headers end). | ||
|
||
get_http_injector() -> | ||
persistent_term:get({?MODULE, http_injector}, fun(_Headers) -> ok end). | ||
|
||
-spec timestamp() -> integer(). | ||
timestamp() -> | ||
erlang:system_time(nanosecond). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
%%%------------------------------------------------------------------------ | ||
%% Copyright 2019, OpenTelemetry Authors | ||
%% Licensed under the Apache License, Version 2.0 (the "License"); | ||
%% you may not use this file except in compliance with the License. | ||
%% You may obtain a copy of the License at | ||
%% | ||
%% http://www.apache.org/licenses/LICENSE-2.0 | ||
%% | ||
%% Unless required by applicable law or agreed to in writing, software | ||
%% distributed under the License is distributed on an "AS IS" BASIS, | ||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
%% See the License for the specific language governing permissions and | ||
%% limitations under the License. | ||
%% | ||
%% @doc | ||
%% @end | ||
%%%------------------------------------------------------------------------- | ||
-module(ot_baggage). | ||
|
||
-export([ctx_key/0, | ||
set/2, | ||
get/1, | ||
remove/1, | ||
clear/0, | ||
get_http_propagators/0]). | ||
|
||
-type key() :: string(). | ||
-type value() :: string(). | ||
|
||
-export_type([key/0, | ||
value/0]). | ||
|
||
-define(BAGGAGE_KEY, '$__ot_baggage_ctx_key'). | ||
|
||
ctx_key() -> | ||
?BAGGAGE_KEY. | ||
|
||
-spec set(key(), value()) -> ok. | ||
set(Key, Value) -> | ||
ot_ctx:set_value(?BAGGAGE_KEY, Key, Value). | ||
|
||
-spec get(key()) -> value(). | ||
get(Key)-> | ||
ot_ctx:get_value(?BAGGAGE_KEY, Key). | ||
|
||
-spec remove(key()) -> ok. | ||
remove(Key) -> | ||
ot_ctx:remove(?BAGGAGE_KEY, Key). | ||
|
||
-spec clear() -> ok. | ||
clear() -> | ||
ot_ctx:clear(?BAGGAGE_KEY). | ||
|
||
-spec get_http_propagators() -> {ot_propagation:http_extractor(), ot_propagation:http_injector()}. | ||
get_http_propagators() -> | ||
ToText = fun(_Headers, undefined) -> | ||
[]; | ||
(_Headers, Baggage) -> | ||
List = maps:fold(fun(Key, Value, Acc) -> | ||
[[Key, "=", Value] | Acc] | ||
end, [], Baggage), | ||
lists:join(",", List) | ||
tsloughter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end, | ||
FromText = fun(String, CurrentBaggage) -> | ||
Pairs = string:lexemes(String, [","]), | ||
lists:foldl(fun(Pair, Acc) -> | ||
[Key, Pair] = string:split(Pair, "="), | ||
Acc#{Key => {Pair, unlimited_propagation}} | ||
end, CurrentBaggage, Pairs) | ||
end, | ||
Inject = ot_ctx:http_injector(?BAGGAGE_KEY, ToText), | ||
Extract = ot_ctx:http_extractor(?BAGGAGE_KEY, FromText), | ||
{Extract, Inject}. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
%%%------------------------------------------------------------------------ | ||
%% Copyright 2019, OpenTelemetry Authors | ||
%% Licensed under the Apache License, Version 2.0 (the "License"); | ||
%% you may not use this file except in compliance with the License. | ||
%% You may obtain a copy of the License at | ||
%% | ||
%% http://www.apache.org/licenses/LICENSE-2.0 | ||
%% | ||
%% Unless required by applicable law or agreed to in writing, software | ||
%% distributed under the License is distributed on an "AS IS" BASIS, | ||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
%% See the License for the specific language governing permissions and | ||
%% limitations under the License. | ||
%% | ||
%% @doc | ||
%% @end | ||
%%%------------------------------------------------------------------------- | ||
-module(ot_correlations). | ||
|
||
-export([ctx_key/0, | ||
set/3, | ||
get_http_propagators/0]). | ||
|
||
-type key() :: string(). | ||
-type value() :: string(). | ||
-type hop_limit() :: no_propagation | unlimited_propagation. | ||
|
||
-export_type([key/0, | ||
value/0]). | ||
|
||
-define(CORRELATIONS_KEY, '$__ot_correlations_ctx_key'). | ||
|
||
ctx_key() -> | ||
?CORRELATIONS_KEY. | ||
|
||
-spec set(key(), value(), hop_limit()) -> ok. | ||
set(Key, Value, HopLimit) -> | ||
ot_ctx:set_value(?CORRELATIONS_KEY, Key, {Value, HopLimit}). | ||
|
||
-spec get_http_propagators() -> {ot_propagation:http_extractor(), ot_propagation:http_injector()}. | ||
get_http_propagators() -> | ||
ToText = fun(_Headers, Correlations) -> | ||
List = maps:fold(fun(Key, {Value, unlimited_propagation}, Acc) -> | ||
[[Key, "=", Value] | Acc]; | ||
(_Key, {_Value, no_propagation}, Acc) -> | ||
Acc | ||
end, [], Correlations), | ||
lists:join(",", List) | ||
end, | ||
FromText = fun(String, CurrentCorrelations) -> | ||
Pairs = string:lexemes(String, [","]), | ||
lists:foldl(fun(Pair, Acc) -> | ||
[Key, Pair] = string:split(Pair, "="), | ||
Acc#{Key => {Pair, unlimited_propagation}} | ||
end, CurrentCorrelations, Pairs) | ||
end, | ||
Inject = ot_ctx:http_injector(?CORRELATIONS_KEY, ToText), | ||
Extract = ot_ctx:http_extractor(?CORRELATIONS_KEY, FromText), | ||
{Extract, Inject}. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
%%%------------------------------------------------------------------------ | ||
%% Copyright 2019, OpenTelemetry Authors | ||
%% Licensed under the Apache License, Version 2.0 (the "License"); | ||
%% you may not use this file except in compliance with the License. | ||
%% You may obtain a copy of the License at | ||
%% | ||
%% http://www.apache.org/licenses/LICENSE-2.0 | ||
%% | ||
%% Unless required by applicable law or agreed to in writing, software | ||
%% distributed under the License is distributed on an "AS IS" BASIS, | ||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
%% See the License for the specific language governing permissions and | ||
%% limitations under the License. | ||
%% | ||
%% @doc | ||
%% @end | ||
%%%------------------------------------------------------------------------- | ||
-module(ot_ctx). | ||
|
||
-export([set_value/3, | ||
get_value/2, | ||
with_value/4, | ||
with_value/5, | ||
remove/2, | ||
clear/1, | ||
set_current/2, | ||
get_current/1, | ||
http_extractor/2, | ||
http_injector/2, | ||
http_extractor/3, | ||
http_injector/3]). | ||
|
||
-type ctx() :: map(). | ||
-type namespace() :: term(). | ||
-type key() :: term(). | ||
-type value() :: term(). | ||
|
||
-callback set_value(namespace(), key(), value()) -> ok. | ||
-callback get_value(namespace(), key()) -> value() | undefined. | ||
-callback remove(namespace(), key()) -> ok. | ||
-callback clear(namespace()) -> ok. | ||
-callback set_current(namespace(), ctx()) -> ok. | ||
-callback get_current(namespace()) -> ctx(). | ||
|
||
-export_type([ctx/0, | ||
key/0, | ||
value/0]). | ||
|
||
-spec set_value(namespace(), key(), value()) -> ok. | ||
set_value(Namespace, Key, Value) -> | ||
set_value(opentelemetry:get_context_manager(), Namespace, Key, Value). | ||
|
||
-spec set_value(module(), namespace(), key(), value()) -> ok. | ||
set_value(CtxModule, Namespace, Key, Value) -> | ||
CtxModule:set_value(Namespace, Key, Value). | ||
|
||
-spec get_value(namespace(), key()) -> value(). | ||
get_value(Namespace, Key) -> | ||
get_value(opentelemetry:get_context_manager(), Namespace, Key). | ||
|
||
-spec get_value(module(), namespace(), key()) -> value(). | ||
get_value(Module, Namespace, Key) -> | ||
Module:get_value(Namespace, Key). | ||
|
||
-spec with_value(namespace(), key(), value(), fun()) -> ok. | ||
with_value(Namespace, Key, Value, Fun) -> | ||
with_value(opentelemetry:get_context_manager(), Namespace, Key, Value, Fun). | ||
|
||
-spec with_value(module(), namespace(), key(), value(), fun()) -> ok. | ||
with_value(CtxModule, Namespace, Key, Value, Fun) -> | ||
PrevCtx = get_current(CtxModule, Namespace), | ||
CtxModule:set_current(PrevCtx, Namespace, PrevCtx#{Key => Value}), | ||
try | ||
Fun() | ||
after | ||
set_current(CtxModule, Namespace, PrevCtx) | ||
end. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this usage implies that the current values for the context can't be global, and only process-global at best? Otherwise running two of these at the same time may break things, right? Might be worth pointing out in a comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, context is tied to a process. I can note that. |
||
|
||
-spec remove(namespace(), key()) -> ok. | ||
remove(Namespace, Key) -> | ||
remove(opentelemetry:get_context_manager(), Namespace, Key). | ||
|
||
-spec remove(module(), namespace(), key()) -> ok. | ||
remove(CtxModule, Namespace, Key) -> | ||
CtxModule:remove(Namespace, Key). | ||
|
||
-spec clear(namespace()) -> ok. | ||
clear(Namespace) -> | ||
clear(opentelemetry:get_context_manager(), Namespace). | ||
|
||
-spec clear(module(), namespace()) -> ok. | ||
clear(CtxModule, Namespace) -> | ||
CtxModule:clear(Namespace). | ||
|
||
-spec set_current(namespace(), ctx()) -> ok. | ||
set_current(Namespace, Ctx) -> | ||
set_current(opentelemetry:get_context_manager(), Namespace, Ctx). | ||
|
||
-spec set_current(module(), namespace(), ctx()) -> ok. | ||
set_current(CtxModule, Namespace, Ctx) -> | ||
CtxModule:set_current(Namespace, Ctx). | ||
|
||
-spec get_current(namespace()) -> ctx(). | ||
get_current(Namespace) -> | ||
get_current(opentelemetry:get_context_manager(), Namespace). | ||
|
||
-spec get_current(module(), namespace()) -> ctx(). | ||
get_current(CtxModule, Namespace) -> | ||
CtxModule:get_current(Namespace). | ||
|
||
http_extractor(Namespace, FromText) -> | ||
http_extractor_(opentelemetry:get_context_manager(), Namespace, FromText). | ||
|
||
http_extractor_(CtxModule, Namespace, FromText) -> | ||
fun(_Headers) -> | ||
String = "", %% Headers | ||
New = FromText(String, CtxModule:get_current(Namespace)), | ||
CtxModule:set_current(Namespace, New) | ||
end. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this just a stub? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yea, I'll finish this. |
||
|
||
http_extractor(Namespace, Key, FromText) -> | ||
http_extractor_(opentelemetry:get_context_manager(), Namespace, Key, FromText). | ||
|
||
http_extractor_(CtxModule, Namespace, Key, FromText) -> | ||
fun(Headers) -> | ||
New = FromText(Headers, CtxModule:get_value(Namespace, Key)), | ||
CtxModule:set_value(Namespace, Key, New) | ||
end. | ||
|
||
http_injector(Namespace, ToText) -> | ||
http_injector_(opentelemetry:get_context_manager(), Namespace, ToText). | ||
|
||
http_injector_(CtxModule, Namespace, ToText) -> | ||
fun(Headers) -> | ||
Headers ++ ToText(Headers, CtxModule:get_current(Namespace)) | ||
end. | ||
|
||
http_injector(Namespace, Key, ToText) -> | ||
http_injector_(opentelemetry:get_context_manager(), Namespace, Key, ToText). | ||
|
||
http_injector_(CtxModule, Namespace, Key, ToText) -> | ||
fun(Headers) -> | ||
Headers ++ ToText(Headers, CtxModule:get_value(Namespace, Key)) | ||
end. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
%%%------------------------------------------------------------------------ | ||
%% Copyright 2019, OpenTelemetry Authors | ||
%% Licensed under the Apache License, Version 2.0 (the "License"); | ||
%% you may not use this file except in compliance with the License. | ||
%% You may obtain a copy of the License at | ||
%% | ||
%% http://www.apache.org/licenses/LICENSE-2.0 | ||
%% | ||
%% Unless required by applicable law or agreed to in writing, software | ||
%% distributed under the License is distributed on an "AS IS" BASIS, | ||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
%% See the License for the specific language governing permissions and | ||
%% limitations under the License. | ||
%% | ||
%% @doc | ||
%% @end | ||
%%%------------------------------------------------------------------------- | ||
-module(ot_ctx_noop). | ||
|
||
-behaviour(ot_ctx). | ||
|
||
-export([set_value/3, | ||
get_value/2, | ||
remove/2, | ||
clear/1, | ||
set_current/2, | ||
get_current/1]). | ||
|
||
-spec set_value(ot_ctx:namespace(), ot_ctx:key(), ot_ctx:value()) -> ok. | ||
set_value(_Namespace, _Key, _Value) -> | ||
ok. | ||
|
||
-spec get_value(ot_ctx:namespace(), ot_ctx:key()) -> ot_ctx:value(). | ||
get_value(_Namespace, _Key) -> | ||
undefined. | ||
|
||
-spec remove(ot_ctx:namespace(), ot_ctx:key()) -> ok. | ||
remove(_Namespace, _Key) -> | ||
ok. | ||
|
||
-spec clear(ot_ctx:namespace()) -> ok. | ||
clear(_Namespace) -> | ||
ok. | ||
|
||
-spec set_current(ot_ctx:namespace(), ot_ctx:ctx()) -> ok. | ||
set_current(_Namespace, _Ctx) -> | ||
ok. | ||
|
||
-spec get_current(ot_ctx:namespace()) -> ot_ctx:ctx(). | ||
get_current(_Namespace) -> | ||
#{}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense to guard that this is actually a module (or an atom if we don't check for loading)? Like there's a risk of setting a bad value and insta-killing all the things, no?
In fact looking at the call below, the expected form is
{Module, Args}
and asserting there could prevent huge mistakes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agh yea, very good catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it is actually used as just a module atom. Clearly need some typespecs so dialyzer will catch this shit where
get_*
is used.I'm now unsure if there should be a tuple or not. Depends on if some state (that can't be updated because it is in a persistent term) is needed. I think I have an idea, will update this in a bit.