Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RecordException to Elixir's Span module #138

Merged
merged 15 commits into from
Feb 11, 2021

Conversation

gugahoa
Copy link
Contributor

@gugahoa gugahoa commented Nov 2, 2020

Reference for RecordException

RecordException has been implemented only on Elixir side as it has a
more uniform way to deal with exceptions as a value.

The attribute exception.escaped is left up to the user to supply
through the attributes parameter in record_exception/4.

@codecov
Copy link

codecov bot commented Nov 2, 2020

Codecov Report

Merging #138 (e695962) into main (3995f77) will decrease coverage by 19.86%.
The diff coverage is 0.00%.

Impacted file tree graph

@@             Coverage Diff             @@
##             main     #138       +/-   ##
===========================================
- Coverage   33.90%   14.03%   -19.87%     
===========================================
  Files          64       30       -34     
  Lines        3256      463     -2793     
===========================================
- Hits         1104       65     -1039     
+ Misses       2152      398     -1754     
Flag Coverage Δ
api 14.03% <0.00%> (-36.96%) ⬇️
elixir 14.03% <0.00%> (-0.25%) ⬇️
erlang ?
exporter ?
sdk ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
apps/opentelemetry_api/lib/open_telemetry/span.ex 18.75% <0.00%> (-6.25%) ⬇️
apps/opentelemetry_api/src/otel_span.erl 20.00% <0.00%> (-41.91%) ⬇️
apps/opentelemetry_api/src/otel_propagator.erl 0.00% <0.00%> (-81.82%) ⬇️
apps/opentelemetry_api/src/otel_baggage.erl 14.81% <0.00%> (-66.67%) ⬇️
.../opentelemetry_api/src/otel_propagator_http_b3.erl 0.00% <0.00%> (-64.52%) ⬇️
apps/opentelemetry_api/src/otel_meter.erl 0.00% <0.00%> (-63.64%) ⬇️
apps/opentelemetry_api/src/otel_counter.erl 0.00% <0.00%> (-57.15%) ⬇️
apps/opentelemetry_api/src/opentelemetry.erl 10.93% <0.00%> (-56.25%) ⬇️
...opentelemetry_api/src/otel_propagator_http_w3c.erl 0.00% <0.00%> (-50.91%) ⬇️
...pps/opentelemetry_api/src/otel_tracer_provider.erl 0.00% <0.00%> (-48.00%) ⬇️
... and 41 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3995f77...37f5559. Read the comment docs.

@gugahoa
Copy link
Contributor Author

gugahoa commented Nov 2, 2020

I would like some comments on how to approach this from Erlang side

I researched about exceptions in Erlang, and they don't differ from actual values at all. It’s hard to settle on a good interface for record_exception

I read here about how the try came to be, and the only way I could think of a good interface for record_exception would be the triple {class, term, trace} mentioned in Figure 8, but I couldn't extract that triple from the try as a user.

@gugahoa
Copy link
Contributor Author

gugahoa commented Nov 2, 2020

I also increased the minimum Elixir version to 1.11, so that is_exception could be used as a guard, but if it's not desirable I can refactor to remove that

@tsloughter
Copy link
Member

Is "message" required or an optional field? The type in Erlang would be the "exception" (2nd part of C:T:S). But we'd want the "class" as well. So maybe it'd be include the class and exception in the same "type" field of the event? Other option would be to use type as the class and message for the exception.

I'd prefer "message" to be the result of a call to Module:format_error(T) passing the exception. The question there would be if we relied on the stacktrace to know the Module to call or require the exception to be of the form {Module, T}.

@gugahoa
Copy link
Contributor Author

gugahoa commented Nov 3, 2020

@tsloughter at least one of message or type should be present in the event.

My whole issue with the Erlang side is about which arguments to accept.

Should the function require that the user know what to pass? What if they mistakenly pass a incorrect argument? How would we know?

@tsloughter
Copy link
Member

It is on them to pass the right arguments, that is always true. But we can also optionally handle it for them in the with_span function since we already have a try.

Hm, this might be reason to replace the Elixir call of with_span with its own implementation so it can use whatever exception stuff it has?

For Erlang I guess we need to offer a couple options, like otel_span:record_exception(C, T, S) and otel_span:record_exception(C, T, Message, S).

The record_exception function should then always combine C and T into a string <<"C:T">> and Message is passed by the user, so if they have a Moudle:format_error(Reason) they can pass that themselves.

@ferd any thoughts on recording exceptions in Erlang?

@tsloughter
Copy link
Member

Based on what Python does https://github.com/open-telemetry/opentelemetry-python/blob/master/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py#L698-L700 maybe type should just be the Erlang class of the exception.

Need to check what Java uses for that field too.

@ferd
Copy link
Member

ferd commented Nov 6, 2020

Feels fair to use the type as a class. The direct matched value is the reason (which is often a string in other languages and therefore it's alright to use tuples or strings there too for us), and stacktrace has direct matches as well.

An interesting question will be to plan for stuff in EEP-54: erlang/otp#2849 -- this uses Args hidden in the stacktrace to add context to the exception's reason

@gugahoa
Copy link
Contributor Author

gugahoa commented Nov 7, 2020

That PR introduces a format_exception/3, which we could use for exception.stacktrace

Until then, would it be ok to use erlang:display/1 to populate exception.stacktrace?

@ferd
Copy link
Member

ferd commented Nov 7, 2020

No, erlang:display/1 is a debug-only BIF sending data to stdout, that can't be the thing you're looking for.

@tsloughter
Copy link
Member

It also prints to stdout, it doesn't return a string. You just want io_lib:format("~p", [Stacktrace]) or maybe a depth limited form of that.

@tsloughter
Copy link
Member

@gugahoa are you able to update this to add the Erlang version? I'd like to get it merged before making a release soon.

@gugahoa gugahoa marked this pull request as ready for review November 29, 2020 15:48
@gugahoa gugahoa requested a review from a team November 29, 2020 15:48
Reference for [`RecordException`](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#record-exception)

`RecordException` has been implemented only on Elixir side as it has a
more uniform way to deal with exceptions as a value.

The attribute `exception.escaped` is left up to the user to supply
through the `attributes` parameter in `record_exception/4`.
@tsloughter
Copy link
Member

Great, looking good. But now I'm not sure about requiring 1.11. At work we have systems still on 1.10 and 1.9 that aren't easy upgrades. Would at least 1.10 be not much work to support for this?

@gugahoa
Copy link
Contributor Author

gugahoa commented Dec 23, 2020

The only thing that makes 1.11 required is the is_exception/1 guard, we can copy paste that into the module that uses it and remove the requirement on 1.11

Does that sound good to you? @tsloughter

@tsloughter
Copy link
Member

@gugahoa ah, yea, that sounds good.

@gugahoa
Copy link
Contributor Author

gugahoa commented Dec 23, 2020

@tsloughter done 🔝

Stacktrace :: list(any()),
Attributes :: opentelemetry:attributes().
record_exception(SpanCtx, Class, Term, Stacktrace, Attributes) ->
ExceptionAttributes = [{<<"exception.type">>, iolist_to_binary(io_lib:format("~p:~p", [Class, Term], [{chars_limit, 50}]))},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of ~p I think it should be ~0tP, in which case a second argument of the term depth is needed. So like:

io_lib:format("~0tp:~0tP", [Class, Term, 10], [{chars_limit, 50}]))

The 0 means to not add newlines and the 10 means the depth of the term is limited to 10. I'm not sure if 10 is the best depth but seems reasonable.

What do you think @ferd ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah a depth of 10 should be acceptable and it's easier to expand than it is to clamp down usually

@tsloughter tsloughter changed the base branch from master to main February 11, 2021 19:37
@tsloughter tsloughter merged commit 3d26b62 into open-telemetry:main Feb 11, 2021
@gugahoa gugahoa deleted the add-record-exception branch February 12, 2021 00:14
@tsloughter tsloughter mentioned this pull request Feb 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants