Skip to content

Commit

Permalink
Add :tags_from_metadata option to Sentry.LoggerHandler (#840)
Browse files Browse the repository at this point in the history
Adds a new option :tags_from_metadata that allows specifying sentry tags based on
already-set Logger metadata keys.

Co-authored-by: Andrea Leopardi <an.leopardi@gmail.com>
  • Loading branch information
icehaunter and whatyouhide authored Dec 25, 2024
1 parent b695453 commit c565390
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 4 deletions.
9 changes: 8 additions & 1 deletion lib/sentry/logger_backend.ex
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,14 @@ defmodule Sentry.LoggerBackend do
# The context in the Logger backend process is not the same as the one in the process
# that did the logging. This behavior is different than the one in Sentry.LoggerHandler,
# since Logger handlers run in the caller process.
opts = LoggerUtils.build_sentry_options(level, sentry_context, Map.new(meta), state.metadata)
opts =
LoggerUtils.build_sentry_options(
level,
sentry_context,
Map.new(meta),
state.metadata,
_tags_from_metadata = []
)

case meta[:crash_reason] do
# If the crash reason is an exception, we want to report the exception itself
Expand Down
12 changes: 11 additions & 1 deletion lib/sentry/logger_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ defmodule Sentry.LoggerHandler do
If set to `:all`, all metadata will be included.
"""
],
tags_from_metadata: [
type: {:list, :atom},
default: [],
doc: """
Use this to include logger metadata as tags in reports. Metadata under the specified keys
in those keys will be added as tags to the event. *Available since v10.9.0*.
"""
],
capture_log_messages: [
type: :boolean,
default: false,
Expand Down Expand Up @@ -220,6 +228,7 @@ defmodule Sentry.LoggerHandler do
:level,
:excluded_domains,
:metadata,
:tags_from_metadata,
:capture_log_messages,
:rate_limiting,
:sync_threshold
Expand Down Expand Up @@ -321,7 +330,8 @@ defmodule Sentry.LoggerHandler do
log_level,
log_meta[:sentry],
log_meta,
config.metadata
config.metadata,
config.tags_from_metadata
)

log_unfiltered(log_event, sentry_opts, config)
Expand Down
22 changes: 20 additions & 2 deletions lib/sentry/logger_utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,27 @@ defmodule Sentry.LoggerUtils do

require Logger

@spec build_sentry_options(Logger.level(), keyword() | nil, map(), [atom()] | :all) ::
@spec build_sentry_options(
Logger.level(),
keyword() | nil,
map(),
[atom()] | :all,
[atom()]
) ::
keyword()
def build_sentry_options(level, sentry_context, meta, allowed_meta) do
def build_sentry_options(level, sentry_context, meta, allowed_meta, tags_from_metadata) do
default_extra =
Map.merge(
%{logger_metadata: logger_metadata(meta, allowed_meta), logger_level: level},
Map.take(meta, [:domain])
)

default_tags = logger_tags_from_metadata(meta, tags_from_metadata)

(sentry_context || get_sentry_options_from_callers(meta[:callers]) || %{})
|> Map.new()
|> Map.update(:extra, default_extra, &Map.merge(&1, default_extra))
|> Map.update(:tags, default_tags, &Map.merge(&1, default_tags))
|> Map.merge(%{
event_source: :logger,
level: elixir_logger_level_to_sentry_level(level),
Expand Down Expand Up @@ -79,6 +88,15 @@ defmodule Sentry.LoggerUtils do
:maps.map(fn _key, val -> attempt_to_convert_iodata(val) end, meta)
end

defp logger_tags_from_metadata(meta, tags_from_metadata) do
for {key, value} <- meta,
key in tags_from_metadata,
is_binary(value),
into: %{} do
{key, value}
end
end

defp attempt_to_convert_iodata(list) when is_list(list) do
IO.chardata_to_string(list)
rescue
Expand Down
29 changes: 29 additions & 0 deletions test/sentry/logger_handler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,35 @@ defmodule Sentry.LoggerHandlerTest do
end
end

describe "with logger metadata as tags" do
@tag handler_config: %{capture_log_messages: true, tags_from_metadata: [:string, :number]}
test "includes configured Logger metadata as tags, but only if strings", %{sender_ref: ref} do
Logger.metadata(string: "value", number: 42, other: "ignored")
Logger.error("Testing error")

assert_receive {^ref, event}
assert event.tags == %{string: "value"}
end

@tag handler_config: %{capture_log_messages: true, tags_from_metadata: []}
test "does not include Logger metadata as tags when disabled",
%{sender_ref: ref} do
Logger.error("Testing error", string: "value", number: 42)

assert_receive {^ref, event}
assert event.tags == %{}
end

@tag handler_config: %{capture_log_messages: true, tags_from_metadata: [:string, :number]}
test "merges configured tags with explicitly set tags", %{sender_ref: ref} do
Logger.metadata(string: "value", number: 42)
Logger.error("Testing error", sentry: [tags: %{explicit: "tag", number: 44}])

assert_receive {^ref, event}
assert event.tags == %{string: "value", number: 44, explicit: "tag"}
end
end

describe "with a crashing GenServer" do
setup do
%{test_genserver: start_supervised!(TestGenServer, restart: :temporary)}
Expand Down

0 comments on commit c565390

Please sign in to comment.