Skip to content
5 changes: 5 additions & 0 deletions .sampo/changesets/dashing-witch-aino.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
hex/posthog: patch
---

Add source location stacktrace frames for plain Logger messages.
68 changes: 66 additions & 2 deletions lib/posthog/handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ defmodule PostHog.Handler do
initial_exception = exception(log_event, config)

reporter_exception =
log_event |> Map.update!(:meta, &Map.delete(&1, :crash_reason)) |> exception(config)
log_event
|> Map.update!(:meta, fn metadata ->
metadata
|> Map.delete(:crash_reason)
|> Map.put(:posthog_reporter, true)
end)
|> exception(config)

[initial_exception, reporter_exception]
end
Expand All @@ -97,7 +103,13 @@ defmodule PostHog.Handler do
initial_exception = exception(log_event, config)

reporter_exception =
log_event |> Map.update!(:meta, &Map.delete(&1, :crash_reason)) |> exception(config)
log_event
|> Map.update!(:meta, fn metadata ->
metadata
|> Map.delete(:crash_reason)
|> Map.put(:posthog_reporter, true)
end)
|> exception(config)

[reporter_exception, initial_exception]
end
Expand Down Expand Up @@ -135,6 +147,16 @@ defmodule PostHog.Handler do
defp do_type(%{meta: %{crash_reason: {reason, _}}}),
do: Exception.format_banner(:exit, reason)

defp do_type(%{msg: {:string, chardata}, meta: %{error_logger: _}}),
do: IO.chardata_to_string(chardata)

defp do_type(%{msg: {:string, chardata}, meta: %{posthog_reporter: true}}),
do: IO.chardata_to_string(chardata)

defp do_type(%{level: level, msg: {:string, _}, meta: %{mfa: {_, _, _}, file: file, line: _}})
when is_binary(file) or is_list(file),
do: "Logger.#{level}"

defp do_type(%{msg: {:string, chardata}}), do: IO.chardata_to_string(chardata)

defp do_type(%{msg: {:report, %{label: {:gen_server, :terminate}}}}) do
Expand Down Expand Up @@ -203,8 +225,50 @@ defmodule PostHog.Handler do
),
do: %{stacktrace: do_stacktrace(stacktrace, in_app_modules, config)}

defp stacktrace(
%{meta: %{mfa: {module, function, arity_or_args}, file: file, line: line}},
in_app_modules,
config
)
when is_binary(file) or is_list(file) do
filename =
file |> IO.chardata_to_string() |> normalize_source_filename(config.root_source_code_paths)

frame =
%{
platform: "custom",
lang: "elixir",
function: Exception.format_mfa(module, function, arity_or_args),
filename: filename,
lineno: line,
module: inspect(module),
in_app: module in in_app_modules,
resolved: true,
synthetic: true
}
|> maybe_add_source_context(filename, line, config)

%{stacktrace: %{type: "raw", frames: [frame]}}
end

defp stacktrace(_event, _, _), do: %{}

defp normalize_source_filename(filename, root_paths) do
relative_to_source_root =
root_paths
|> Enum.map(&Path.expand/1)
|> Enum.sort_by(&String.length/1, :desc)
|> Enum.find_value(fn root ->
relative = Path.relative_to(filename, root)

if relative != filename do
relative
end
end)

relative_to_source_root || Path.relative_to_cwd(filename)
end

defp do_stacktrace([_ | _] = stacktrace, in_app_modules, config) do
frames =
for {module, function, arity_or_args, location} <- stacktrace do
Expand Down
56 changes: 51 additions & 5 deletions test/posthog/handler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule PostHog.HandlerTest do
properties: %{
"$exception_list": [
%{
type: "Hello World",
type: "Logger.info",
value: "Hello World",
mechanism: %{handled: true, type: "generic"}
}
Expand Down Expand Up @@ -53,7 +53,7 @@ defmodule PostHog.HandlerTest do
foo: "bar",
"$exception_list": [
%{
type: "Hello World",
type: "Logger.info",
value: "Hello World",
mechanism: %{handled: true, type: "generic"}
}
Expand All @@ -62,6 +62,52 @@ defmodule PostHog.HandlerTest do
} = event
end

test "adds synthetic stacktrace frame for plain logger messages", %{
handler_ref: ref,
config: %{supervisor_name: supervisor_name}
} do
expected_line = log_plain_message()
LoggerHandlerKit.Assert.assert_logged(ref)

assert [event] = all_captured(supervisor_name)

assert %{
properties: %{
"$exception_list": [
%{
type: "Logger.info",
value: "Hello World",
stacktrace: %{
type: "raw",
frames: [
%{
platform: "custom",
lang: "elixir",
filename: filename,
function: function,
lineno: ^expected_line,
module: "PostHog.HandlerTest",
in_app: false,
resolved: true,
synthetic: true
}
]
}
}
]
}
} = event

assert filename == "test/posthog/handler_test.exs"
assert function == "PostHog.HandlerTest.log_plain_message/0"
end

defp log_plain_message do
expected_line = __ENV__.line + 1
Logger.info("Hello World")
expected_line
end

@tag config: [capture_level: :warning]
test "ignores messages lower than capture_level", %{
handler_ref: ref,
Comment thread
hpouillot marked this conversation as resolved.
Expand Down Expand Up @@ -1487,7 +1533,7 @@ defmodule PostHog.HandlerTest do
extra: "Foo",
"$exception_list": [
%{
type: "Error with metadata",
type: "Logger.error",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I switched to Logger.#{level} here, because the error message could be dynamic and cause undergrouping as we always use the exception type in fp.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oof, that's a whole different topic to be honest. Perhaps it's be best to keep this PR focused on synthetic stacktrace and not change error type logic here?

value: "Error with metadata",
mechanism: %{handled: true}
}
Expand Down Expand Up @@ -1518,7 +1564,7 @@ defmodule PostHog.HandlerTest do
should: "work",
"$exception_list": [
%{
type: "Error with metadata",
type: "Logger.error",
value: "Error with metadata",
mechanism: %{handled: true}
}
Expand Down Expand Up @@ -1607,7 +1653,7 @@ defmodule PostHog.HandlerTest do
foo: "bar",
"$exception_list": [
%{
type: "Error with metadata",
type: "Logger.error",
value: "Error with metadata",
mechanism: %{handled: true, type: "generic"}
}
Expand Down
Loading