Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/sentry/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule Sentry.Application do
end

if config[:oban][:capture_errors] do
Sentry.Integrations.Oban.ErrorReporter.attach()
Sentry.Integrations.Oban.ErrorReporter.attach(config[:oban])
end

if config[:quantum][:cron][:enabled] do
Expand Down
8 changes: 8 additions & 0 deletions lib/sentry/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ defmodule Sentry.Config do
tuples. *Available since 10.3.0*.
"""
],
oban_tags: [
type: :boolean,
default: false,
doc: """
Whether to include Oban job tags in Sentry error tags. When enabled, the `job.tags`
will be joined with a "," and added as an `oban_tags` tag to Sentry events.
"""
],
cron: [
doc: """
Configuration options for configuring [*crons*](https://docs.sentry.io/product/crons/)
Expand Down
25 changes: 17 additions & 8 deletions lib/sentry/integrations/oban/error_reporter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ defmodule Sentry.Integrations.Oban.ErrorReporter do
# See this blog post:
# https://getoban.pro/articles/enhancing-error-reporting

@spec attach() :: :ok
def attach do
@spec attach(keyword()) :: :ok
def attach(config \\ []) when is_list(config) do
_ =
:telemetry.attach(
__MODULE__,
[:oban, :job, :exception],
&__MODULE__.handle_event/4,
:no_config
config
)

:ok
Expand All @@ -21,32 +21,41 @@ defmodule Sentry.Integrations.Oban.ErrorReporter do
[atom(), ...],
term(),
%{required(:job) => struct(), optional(term()) => term()},
:no_config
keyword()
) :: :ok
def handle_event(
[:oban, :job, :exception],
_measurements,
%{job: job, kind: kind, reason: reason, stacktrace: stacktrace} = _metadata,
:no_config
config
) do
if report?(reason) do
report(job, kind, reason, stacktrace)
report(job, kind, reason, stacktrace, config)
else
:ok
end
end

defp report(job, kind, reason, stacktrace) do
defp report(job, kind, reason, stacktrace, config) do
stacktrace =
case {apply(Oban.Worker, :from_string, [job.worker]), stacktrace} do
{{:ok, atom_worker}, []} -> [{atom_worker, :process, 1, []}]
_ -> stacktrace
end

base_tags = %{oban_worker: job.worker, oban_queue: job.queue, oban_state: job.state}

tags =
if config[:oban_tags] === true and is_list(job.tags) and length(job.tags) > 0 do
Map.put(base_tags, :oban_tags, Enum.join(job.tags, ","))
else
base_tags
end

opts =
[
stacktrace: stacktrace,
tags: %{oban_worker: job.worker, oban_queue: job.queue, oban_state: job.state},
tags: tags,
fingerprint: [job.worker, "{{ default }}"],
extra:
Map.take(job, [:args, :attempt, :id, :max_attempts, :meta, :queue, :tags, :worker]),
Expand Down
26 changes: 23 additions & 3 deletions test/sentry/integrations/oban/error_reporter_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
alias Sentry.Integrations.Oban.ErrorReporter

defmodule MyWorker do
use Oban.Worker
use Oban.Worker, tags: ["tag1", "tag2"]

@impl Oban.Worker
def perform(%Oban.Job{}), do: :ok
Expand Down Expand Up @@ -33,6 +33,7 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
assert event.tags.oban_queue == "default"
assert event.tags.oban_state == "available"
assert event.tags.oban_worker == "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
assert event.tags.oban_tags == "tag1,tag2"
assert %{job: %Oban.Job{}} = event.integration_meta.oban

assert event.fingerprint == [@worker_as_string, "{{ default }}"]
Expand Down Expand Up @@ -64,6 +65,7 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
assert event.tags.oban_queue == "default"
assert event.tags.oban_state == "available"
assert event.tags.oban_worker == "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
assert event.tags.oban_tags == "tag1,tag2"
assert %{job: %Oban.Job{}} = event.integration_meta.oban

assert event.fingerprint == [@worker_as_string, "{{ default }}"]
Expand Down Expand Up @@ -154,11 +156,29 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
assert Sentry.Test.pop_sentry_reports() == []
end
end

test "includes oban_tags when config option is enabled" do
Sentry.Test.start_collecting()

emit_telemetry_for_failed_job(:error, %RuntimeError{message: "oops"}, [], oban_tags: true)

assert [event] = Sentry.Test.pop_sentry_reports()
assert event.tags.oban_tags == "tag1,tag2"
end

test "excludes oban_tags when config option is disabled" do
Sentry.Test.start_collecting()

emit_telemetry_for_failed_job(:error, %RuntimeError{message: "oops"}, [], oban_tags: false)

assert [event] = Sentry.Test.pop_sentry_reports()
assert is_nil(Map.get(event.tags, :oban_tags))
end
end

## Helpers

defp emit_telemetry_for_failed_job(kind, reason, stacktrace) do
defp emit_telemetry_for_failed_job(kind, reason, stacktrace, config \\ []) do
job =
%{"id" => "123", "entity" => "user", "type" => "delete"}
|> MyWorker.new()
Expand All @@ -169,7 +189,7 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
[:oban, :job, :exception],
%{},
%{job: job, kind: kind, reason: reason, stacktrace: stacktrace},
:no_config
config
)

job
Expand Down