Packages

appsignal

2.15.9
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 check_in scheduler.ex
Raw

lib/appsignal/check_in/scheduler.ex

defmodule Appsignal.CheckIn.Scheduler.Debounce do
@initial_debounce_milliseconds 100
@between_transmissions_debounce_milliseconds 10_000
@system Application.compile_env(
:appsignal,
:system,
System
)
def milliseconds_until_next_transmission(nil), do: @initial_debounce_milliseconds
def milliseconds_until_next_transmission(last_transmission_milliseconds) do
max(
@initial_debounce_milliseconds,
@between_transmissions_debounce_milliseconds -
milliseconds_since(last_transmission_milliseconds)
)
end
defp milliseconds_since(timestamp) do
@system.system_time(:millisecond) - timestamp
end
end
defmodule Appsignal.CheckIn.Scheduler do
use GenServer
alias Appsignal.CheckIn.Event
@debounce Application.compile_env(
:appsignal,
:appsignal_checkin_debounce,
Appsignal.CheckIn.Scheduler.Debounce
)
@transmitter Application.compile_env(
:appsignal,
:appsignal_transmitter,
Appsignal.Transmitter
)
@integration_logger Application.compile_env(
:appsignal,
:appsignal_integration_logger,
Appsignal.IntegrationLogger
)
@system Application.compile_env(
:appsignal,
:system,
System
)
@impl true
def init(_init_arg) do
# Ensure that the GenServer traps exits so that we can attempt to
# transmit any remaining events before terminating.
Process.flag(:trap_exit, true)
{:ok, initial_state()}
end
def start_link(_init_arg) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def schedule(event) do
if Appsignal.Config.active?() do
GenServer.cast(__MODULE__, {:schedule, event})
else
@integration_logger.debug("AppSignal not active, not scheduling #{Event.describe([event])}")
end
:ok
end
@impl true
def handle_cast({:schedule, event}, state) do
@integration_logger.trace("Scheduling #{Event.describe([event])} to be transmitted")
schedule_transmission(state)
{:noreply, %{state | events: add_event(state.events, event)}}
end
@impl true
def handle_info(:transmit, %{events: events}) do
# Remove the stored events from the state before transmitting them,
# to avoid transmitting them twice if the process receives a shutdown
# signal during the transmission.
{:noreply, initial_state(), {:continue, {:transmit, events}}}
end
@impl true
def handle_continue({:transmit, events}, state) do
events = Event.deduplicate_cron(events)
description = Event.describe(events)
config = Appsignal.Config.config()
endpoint = "#{config[:logging_endpoint]}/check_ins/json"
case @transmitter.transmit(endpoint, {Enum.reverse(events), :ndjson}, config) do
{:ok, %{status: status_code}} when status_code in 200..299 ->
@integration_logger.trace("Transmitted #{description}")
{:ok, %{status: status_code}} ->
@integration_logger.error(
"Failed to transmit #{description}: status code was #{status_code}"
)
{:error, reason} ->
@integration_logger.error("Failed to transmit #{description}: #{inspect(reason)}")
end
{
:noreply,
%{state | last_transmission_milliseconds: @system.system_time(:millisecond)},
:hibernate
}
end
@impl true
def terminate(_reason, %{events: events}) when length(events) > 0 do
# If any events are stored, attempt to transmit them before the
# process is terminated.
handle_continue({:transmit, events}, initial_state())
end
def terminate(_reason, _state), do: nil
defp initial_state do
%{events: [], last_transmission_milliseconds: nil}
end
defp schedule_transmission(%{events: []} = state) do
Process.send_after(
self(),
:transmit,
@debounce.milliseconds_until_next_transmission(state.last_transmission_milliseconds)
)
end
defp schedule_transmission(_state) do
# The transmission should only be scheduled when the first event is
# being added, so we don't need to schedule it again.
nil
end
defp add_event(events, event) do
# Remove redundant events, keeping the newly added one, which
# should be the one with the most recent timestamp.
[
event
| Enum.reject(events, fn existing_event ->
is_redundant = Event.redundant?(existing_event, event)
if is_redundant do
@integration_logger.debug("Replacing previously scheduled #{Event.describe([event])}")
end
is_redundant
end)
]
end
end