Packages

appsignal

2.9.2
2.17.4 2.17.3 2.17.2 2.17.1 2.17.0 2.16.0 2.15.11 2.15.10 2.15.9 2.15.8 2.15.7 2.15.6 2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.1 2.14.0 2.13.4 2.13.3 2.13.2 2.13.1 2.13.0 2.12.3 2.12.2 2.12.1 2.12.0 2.11.0 2.10.2 2.10.1 2.10.0 2.9.2 2.9.1 2.9.0 2.8.3 2.8.2 2.8.1 2.8.0 2.7.13 2.7.12 2.7.11 2.7.10 2.7.9 2.7.8 2.7.7 2.7.6 2.7.5 2.7.4 2.7.3 2.7.2 2.7.1 2.7.0 2.7.0-beta.1 2.6.1 2.6.0 2.5.3 2.5.2 2.5.1 2.5.0 2.4.3 2.4.2 2.4.1 2.4.0 2.3.1 2.3.0 2.2.19 2.2.18 2.2.17 2.2.16 2.2.15 2.2.14 2.2.13 2.2.12 retired 2.2.11 2.2.10 2.2.9 2.2.8 2.2.7 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 2.2.1 2.2.0 2.1.15 2.1.14 2.1.13 2.1.12 2.1.11 2.1.10 2.1.9 2.1.8 2.1.7 2.1.7-beta.1 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.8 2.0.7 2.0.6 2.0.6-beta.1 2.0.5 retired 2.0.4 retired 2.0.3 retired 2.0.2 retired 2.0.1 retired 2.0.0 retired 2.0.0-beta.11 2.0.0-beta.10 2.0.0-beta.9 2.0.0-beta.8 2.0.0-beta.7 2.0.0-beta.6 2.0.0-beta.5 2.0.0-beta.4 2.0.0-beta.3 2.0.0-beta.2 2.0.0-beta.1 2.0.0-alpha.7 2.0.0-alpha.6 2.0.0-alpha.5 2.0.0-alpha.4 2.0.0-alpha.3 2.0.0-alpha.2 2.0.0-alpha.1 1.13.5 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.13.0-beta.2 1.13.0-beta.1 1.12.1 1.12.0 1.12.0-beta.3 1.12.0-beta.2 1.12.0-beta.1 1.11.8 1.11.7 1.11.6 1.11.5 1.11.4 1.11.3 1.11.2 1.11.1 1.11.0 1.11.0-beta.1 1.10.13 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.4 1.9.3 1.9.2 1.9.1 1.9.1-beta.1 1.9.0 1.9.0-beta.1 1.9.0-alpha.1 1.8.2 1.8.1 1.8.1-beta.2 1.8.1-beta.1 1.8.0 1.7.2 1.7.1 1.7.0 1.7.0-beta.2 1.7.0-beta.1 1.7.0-alpha.4 1.7.0-alpha.3 1.7.0-alpha.2 1.7.0-alpha.1 1.6.7 1.6.6 1.6.6-beta.2 1.6.6-beta.1 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.6.0-beta.1 1.6.0-alpha.1 1.5.1 1.5.0 1.5.0-beta.2 1.5.0-beta.1 1.4.11-beta.1 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.4.0-alpha.1 1.3.6 1.3.6-beta.1 1.3.5 1.3.4 1.3.4-beta.1 1.3.3 1.3.2 1.3.1 1.3.0 1.3.0-beta.3 1.3.0-beta.2 1.3.0-beta.1 1.2.3 1.2.2 1.2.1 1.2.0 1.1.1 1.1.0 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0 1.0.0-rc.1 0.13.0 0.12.3 0.12.2 0.12.1 0.12.0 0.11.6 0.11.5 0.11.4 0.11.3 0.11.2 0.11.1 0.11.0 0.10.0 0.9.2 0.9.1 0.9.0 0.8.0 0.7.1 0.7.0 0.6.2 0.6.1 0.6.0 0.5.0 0.4.0 0.3.0 0.2.0 0.1.0 0.0.9 0.0.8 0.0.7 0.0.6 0.0.5 0.0.4 0.0.3 0.0.2 0.0.1

Collects error and performance data from your Elixir applications and sends it to AppSignal

Current section

Files

Jump to
appsignal lib appsignal oban.ex
Raw

lib/appsignal/oban.ex

defmodule Appsignal.Oban do
require Appsignal.Utils
@tracer Appsignal.Utils.compile_env(:appsignal, :appsignal_tracer, Appsignal.Tracer)
@span Appsignal.Utils.compile_env(:appsignal, :appsignal_span, Appsignal.Span)
@appsignal Appsignal.Utils.compile_env(:appsignal, :appsignal, Appsignal)
@moduledoc false
import Appsignal.Utils, only: [warning: 1]
def attach do
exception_handler =
case Appsignal.Config.report_oban_errors() do
"all" -> &__MODULE__.oban_job_exception/4
"discard" -> &__MODULE__.oban_job_discard/4
"none" -> &__MODULE__.oban_job_stop/4
end
handlers = %{
[:oban, :job, :start] => &__MODULE__.oban_job_start/4,
[:oban, :job, :stop] => &__MODULE__.oban_job_stop/4,
[:oban, :job, :exception] => exception_handler,
[:oban, :engine, :insert_job, :start] => &__MODULE__.oban_insert_job_start/4,
[:oban, :engine, :insert_job, :stop] => &__MODULE__.oban_insert_job_stop/4,
[:oban, :engine, :insert_job, :exception] => &__MODULE__.oban_insert_job_stop/4
}
for {event, fun} <- handlers do
detach = :telemetry.detach({__MODULE__, event})
attach = :telemetry.attach({__MODULE__, event}, event, fun, :ok)
case {detach, attach} do
{:ok, :ok} ->
_ = Appsignal.IntegrationLogger.debug("Appsignal.Oban reattached to #{inspect(event)}")
:ok
{{:error, :not_found}, :ok} ->
_ = Appsignal.IntegrationLogger.debug("Appsignal.Oban attached to #{inspect(event)}")
:ok
{_, {:error, _} = error} ->
warning("Appsignal.Oban not attached to #{inspect(event)}: #{inspect(error)}")
error
end
end
end
def oban_job_start(
_event,
_measurements,
%{
id: id,
args: args,
queue: queue,
worker: worker,
attempt: attempt
} = metadata,
_config
) do
span = @tracer.create_span("oban")
span
|> @span.set_name("#{to_string(worker)}#perform")
|> @span.set_sample_data("params", args)
|> @span.set_attribute("id", id)
|> @span.set_attribute("queue", to_string(queue))
|> @span.set_attribute("attempt", attempt)
|> @span.set_attribute("worker", to_string(worker))
|> @span.set_attribute("appsignal:category", "job.oban")
# The `:job` metadata key was added in Oban v2.3.1.
job = metadata[:job]
if job do
@span.set_attribute(span, "priority", job.priority)
add_job_queue_time_value(job, queue)
set_job_meta_attributes(span, job)
end
# The `:tags` metadata key was added in v2.1.0.
for tag <- Map.get(metadata, :tags, []) do
@span.set_attribute(span, "job_tag_#{tag}", true)
end
end
def oban_job_stop(
_event,
%{duration: duration},
%{
worker: worker,
queue: queue
} = metadata,
_config
) do
# The `:state` metadata key was added in Oban v2.4.0.
# If not present, assume the job succeeded.
state = Map.get(metadata, :state, "success")
span =
@tracer.current_span()
|> @span.set_attribute("state", to_string(state))
# The `:result` metadata key was added in Oban v2.5.0.
if Map.has_key?(metadata, :result) do
@span.set_attribute(span, "result", inspect(metadata[:result]))
end
@tracer.close_span(span)
increment_job_stop_counter(worker, queue, state)
add_job_duration_value(worker, duration, state)
end
def oban_job_exception(
_event,
%{duration: duration},
%{
worker: worker,
queue: queue,
kind: kind,
error: reason,
stacktrace: stacktrace
} = metadata,
_config
) do
# The `:state` metadata key was added in Oban v2.4.0.
# If not present, assume the job failed.
state = Map.get(metadata, :state, "failure")
span =
@tracer.current_span()
|> @span.set_attribute("state", to_string(state))
@span.add_error(span, kind, reason, stacktrace)
@tracer.close_span(span)
increment_job_stop_counter(worker, queue, state)
add_job_duration_value(worker, duration, state)
end
def oban_job_discard(event, measurements, metadata, config) do
# The `:state` metadata key was added in Oban v2.4.0.
# If not present, assume the job was discarded.
state = Map.get(metadata, :state, "discard")
if to_string(state) == "discard" do
oban_job_exception(event, measurements, metadata, config)
else
oban_job_stop(event, measurements, metadata, config)
end
end
def oban_insert_job_start(_event, _measurements, metadata, _config) do
do_oban_insert_job_start(@tracer.current_span, metadata)
end
def do_oban_insert_job_start(nil, _metadata), do: nil
def do_oban_insert_job_start(current_span, metadata) do
span = @tracer.create_span("oban", current_span)
@span.set_attribute(span, "appsignal:category", "insert_job.oban")
case metadata[:changeset] do
%{changes: %{worker: worker}} ->
@span.set_name(span, "Insert job (#{worker})")
_ ->
# The changeset containing the worker appears in the metadata for
# this event in every version where this event is emitted (from
# v2.11.0 until v2.13.6, latest at the time of writing) but it's
# undocumented. To be safe, account for it not being present.
@span.set_name(span, "Insert job")
end
end
def oban_insert_job_stop(_event, _measurements, _metadata, _config) do
@tracer.close_span(@tracer.current_span())
end
defp increment_job_stop_counter(worker, queue, state) do
tag_combinations = [
%{worker: to_string(worker), queue: to_string(queue), state: to_string(state)},
%{worker: to_string(worker), state: to_string(state)},
%{queue: to_string(queue), state: to_string(state)},
%{state: to_string(state)}
]
Enum.each(tag_combinations, fn tags ->
@appsignal.increment_counter("oban_job_count", 1, tags)
end)
end
defp add_job_duration_value(worker, duration, state) do
tag_combinations = [
%{worker: to_string(worker), state: to_string(state)},
%{worker: to_string(worker), hostname: Appsignal.Utils.Hostname.hostname()},
%{worker: to_string(worker)}
]
Enum.each(tag_combinations, fn tags ->
@appsignal.add_distribution_value(
"oban_job_duration",
System.convert_time_unit(duration, :native, :millisecond),
tags
)
end)
end
defp add_job_queue_time_value(job, queue) do
delay = DateTime.diff(job.attempted_at, job.scheduled_at, :millisecond)
@appsignal.add_distribution_value("oban_job_queue_time", delay, %{
queue: to_string(queue)
})
end
defp set_job_meta_attributes(span, job) do
for {key, value} <- job.meta do
value =
cond do
is_binary(value) or is_number(value) or is_boolean(value) ->
value
is_atom(value) ->
inspect(value)
true ->
nil
end
if value do
@span.set_attribute(span, "job_meta_#{key}", value)
end
end
end
end