-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #957 from appsignal/rename-heartbeats-to-cron-chec…
…kins Rename heartbeats to cron check-ins
- Loading branch information
Showing
7 changed files
with
275 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
bump: patch | ||
type: deprecate | ||
--- | ||
|
||
Calls to `Appsignal.heartbeat` and to methods in `Appsignal.Heartbeat` will emit a deprecation warning at compile-time. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
bump: patch | ||
type: change | ||
--- | ||
|
||
Rename heartbeats to cron check-ins. Calls to `Appsignal.heartbeat` and `Appsignal.Heartbeat` should be replaced with calls to `Appsignal.CheckIn.cron` and `Appsignal.CheckIn.Cron`, for example: | ||
|
||
```elixir | ||
# Before | ||
Appsignal.heartbeat("do_something", fn -> | ||
do_something() | ||
end) | ||
|
||
# After | ||
Appsignal.CheckIn.cron("do_something", fn -> | ||
do_something | ||
end) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
defmodule Appsignal.CheckIn do | ||
alias Appsignal.CheckIn.Cron | ||
|
||
@spec cron(String.t()) :: :ok | ||
def cron(identifier) do | ||
Cron.finish(Cron.new(identifier)) | ||
end | ||
|
||
@spec cron(String.t(), (-> out)) :: out when out: var | ||
def cron(identifier, fun) do | ||
cron = Cron.new(identifier) | ||
|
||
Cron.start(cron) | ||
output = fun.() | ||
Cron.finish(cron) | ||
|
||
output | ||
end | ||
end | ||
|
||
defmodule Appsignal.CheckIn.Cron do | ||
alias __MODULE__ | ||
alias Appsignal.CheckIn.Cron.Event | ||
|
||
@transmitter Application.compile_env( | ||
:appsignal, | ||
:appsignal_transmitter, | ||
Appsignal.Transmitter | ||
) | ||
@type t :: %Cron{identifier: String.t(), digest: String.t()} | ||
|
||
defstruct [:identifier, :digest] | ||
|
||
@spec new(String.t()) :: t | ||
def new(identifier) do | ||
%Cron{ | ||
identifier: identifier, | ||
digest: random_digest() | ||
} | ||
end | ||
|
||
defp random_digest do | ||
Base.encode16(:crypto.strong_rand_bytes(8), case: :lower) | ||
end | ||
|
||
@spec start(Cron.t()) :: :ok | ||
def start(cron) do | ||
transmit(Event.new(cron, :start)) | ||
end | ||
|
||
@spec finish(Cron.t()) :: :ok | ||
def finish(cron) do | ||
transmit(Event.new(cron, :finish)) | ||
end | ||
|
||
@spec transmit(Event.t()) :: :ok | ||
defp transmit(event) do | ||
if Appsignal.Config.active?() do | ||
config = Appsignal.Config.config() | ||
endpoint = "#{config[:logging_endpoint]}/check_ins/json" | ||
|
||
case @transmitter.transmit(endpoint, event, config) do | ||
{:ok, status_code, _, _} when status_code in 200..299 -> | ||
Appsignal.IntegrationLogger.trace( | ||
"Transmitted cron check-in `#{event.identifier}` (#{event.digest}) #{event.kind} event" | ||
) | ||
|
||
{:ok, status_code, _, _} -> | ||
Appsignal.IntegrationLogger.error( | ||
"Failed to transmit cron check-in #{event.kind} event: status code was #{status_code}" | ||
) | ||
|
||
{:error, reason} -> | ||
Appsignal.IntegrationLogger.error( | ||
"Failed to transmit cron check-in #{event.kind} event: #{reason}" | ||
) | ||
end | ||
else | ||
Appsignal.IntegrationLogger.debug( | ||
"AppSignal not active, not transmitting cron check-in event" | ||
) | ||
end | ||
|
||
:ok | ||
end | ||
end | ||
|
||
defmodule Appsignal.CheckIn.Cron.Event do | ||
alias __MODULE__ | ||
alias Appsignal.CheckIn.Cron | ||
|
||
@derive Jason.Encoder | ||
|
||
@type kind :: :start | :finish | ||
@type t :: %Event{ | ||
identifier: String.t(), | ||
digest: String.t(), | ||
kind: kind, | ||
timestamp: integer, | ||
check_in_type: :cron | ||
} | ||
|
||
defstruct [:identifier, :digest, :kind, :timestamp, :check_in_type] | ||
|
||
@spec new(Cron.t(), kind) :: t | ||
def new(%Cron{identifier: identifier, digest: digest}, kind) do | ||
%Event{ | ||
identifier: identifier, | ||
digest: digest, | ||
kind: kind, | ||
timestamp: System.system_time(:second), | ||
check_in_type: :cron | ||
} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,100 +1,26 @@ | ||
defmodule Appsignal.Heartbeat do | ||
alias __MODULE__ | ||
alias Appsignal.Heartbeat.Event | ||
alias Appsignal.CheckIn | ||
alias Appsignal.CheckIn.Cron | ||
|
||
@transmitter Application.compile_env( | ||
:appsignal, | ||
:appsignal_transmitter, | ||
Appsignal.Transmitter | ||
) | ||
@type t :: %Heartbeat{name: String.t(), id: String.t()} | ||
@type t :: Cron.t() | ||
|
||
defstruct [:name, :id] | ||
@spec new(String.t()) :: Cron.t() | ||
@deprecated "Use `Appsignal.CheckIn.Cron.new/1` instead." | ||
defdelegate new(name), to: Cron | ||
|
||
@spec new(String.t()) :: t | ||
def new(name) do | ||
%Appsignal.Heartbeat{ | ||
name: name, | ||
id: random_id() | ||
} | ||
end | ||
@spec start(Cron.t()) :: :ok | ||
@deprecated "Use `Appsignal.CheckIn.Cron.start/1` instead." | ||
defdelegate start(cron), to: Cron | ||
|
||
defp random_id do | ||
Base.encode16(:crypto.strong_rand_bytes(8), case: :lower) | ||
end | ||
|
||
@spec start(Heartbeat.t()) :: :ok | ||
def start(heartbeat) do | ||
transmit(Event.new(heartbeat, :start)) | ||
end | ||
|
||
@spec finish(Heartbeat.t()) :: :ok | ||
def finish(heartbeat) do | ||
transmit(Event.new(heartbeat, :finish)) | ||
end | ||
@spec finish(Cron.t()) :: :ok | ||
@deprecated "Use `Appsignal.CheckIn.Cron.finish/1` instead." | ||
defdelegate finish(cron), to: Cron | ||
|
||
@spec heartbeat(String.t()) :: :ok | ||
def heartbeat(name) do | ||
finish(Heartbeat.new(name)) | ||
end | ||
@deprecated "Use `Appsignal.CheckIn.cron/1` instead." | ||
defdelegate heartbeat(name), to: CheckIn, as: :cron | ||
|
||
@spec heartbeat(String.t(), (-> out)) :: out when out: var | ||
def heartbeat(name, fun) do | ||
heartbeat = Heartbeat.new(name) | ||
|
||
start(heartbeat) | ||
output = fun.() | ||
finish(heartbeat) | ||
|
||
output | ||
end | ||
|
||
@spec transmit(Event.t()) :: :ok | ||
defp transmit(event) do | ||
if Appsignal.Config.active?() do | ||
config = Appsignal.Config.config() | ||
endpoint = "#{config[:logging_endpoint]}/heartbeats/json" | ||
|
||
case @transmitter.transmit(endpoint, event, config) do | ||
{:ok, status_code, _, _} when status_code in 200..299 -> | ||
Appsignal.IntegrationLogger.trace( | ||
"Transmitted heartbeat `#{event.name}` (#{event.id}) #{event.kind} event" | ||
) | ||
|
||
{:ok, status_code, _, _} -> | ||
Appsignal.IntegrationLogger.error( | ||
"Failed to transmit heartbeat event: status code was #{status_code}" | ||
) | ||
|
||
{:error, reason} -> | ||
Appsignal.IntegrationLogger.error("Failed to transmit heartbeat event: #{reason}") | ||
end | ||
else | ||
Appsignal.IntegrationLogger.debug("AppSignal not active, not transmitting heartbeat event") | ||
end | ||
|
||
:ok | ||
end | ||
end | ||
|
||
defmodule Appsignal.Heartbeat.Event do | ||
alias __MODULE__ | ||
alias Appsignal.Heartbeat | ||
|
||
@derive Jason.Encoder | ||
|
||
@type kind :: :start | :finish | ||
@type t :: %Event{name: String.t(), id: String.t(), kind: kind, timestamp: integer} | ||
|
||
defstruct [:name, :id, :kind, :timestamp] | ||
|
||
@spec new(Heartbeat.t(), kind) :: t | ||
def new(%Heartbeat{name: name, id: id}, kind) do | ||
%Event{ | ||
name: name, | ||
id: id, | ||
kind: kind, | ||
timestamp: System.system_time(:second) | ||
} | ||
end | ||
@deprecated "Use `Appsignal.CheckIn.cron/2` instead." | ||
defdelegate heartbeat(name, fun), to: CheckIn, as: :cron | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
defmodule Appsignal.CheckInTest do | ||
use ExUnit.Case | ||
alias Appsignal.CheckIn | ||
alias Appsignal.CheckIn.Cron | ||
alias Appsignal.CheckIn.Cron.Event | ||
alias Appsignal.FakeTransmitter | ||
import AppsignalTest.Utils, only: [with_config: 2] | ||
|
||
setup do | ||
start_supervised!(FakeTransmitter) | ||
:ok | ||
end | ||
|
||
describe "start/1 and finish/1, when AppSignal is not active" do | ||
test "it does not transmit any events" do | ||
cron = Cron.new("cron-checkin-name") | ||
|
||
with_config(%{active: false}, fn -> | ||
Cron.start(cron) | ||
Cron.finish(cron) | ||
end) | ||
|
||
assert [] = FakeTransmitter.transmitted_payloads() | ||
end | ||
end | ||
|
||
describe "start/1" do | ||
test "transmits a start event for the cron check-in" do | ||
cron = Cron.new("cron-checkin-name") | ||
Cron.start(cron) | ||
|
||
assert [ | ||
%Event{identifier: "cron-checkin-name", kind: :start, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
end | ||
end | ||
|
||
describe "finish/1" do | ||
test "transmits a finish event for the cron check-in" do | ||
cron = Cron.new("cron-checkin-name") | ||
Cron.finish(cron) | ||
|
||
assert [ | ||
%Event{identifier: "cron-checkin-name", kind: :finish, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
end | ||
end | ||
|
||
describe "cron/2" do | ||
test "transmits a start and finish event for the cron check-in" do | ||
output = CheckIn.cron("cron-checkin-name", fn -> "output" end) | ||
|
||
assert [ | ||
%Event{identifier: "cron-checkin-name", kind: :start, check_in_type: :cron}, | ||
%Event{identifier: "cron-checkin-name", kind: :finish, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
|
||
assert "output" == output | ||
end | ||
|
||
test "does not transmit a finish event when the function throws an error" do | ||
assert_raise RuntimeError, fn -> | ||
CheckIn.cron("cron-checkin-name", fn -> raise "error" end) | ||
end | ||
|
||
assert [ | ||
%Event{identifier: "cron-checkin-name", kind: :start, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
end | ||
end | ||
|
||
describe "cron/1" do | ||
test "transmits a finish event for the cron check-in" do | ||
CheckIn.cron("cron-checkin-name") | ||
|
||
assert [ | ||
%Event{identifier: "cron-checkin-name", kind: :finish, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
end | ||
end | ||
|
||
describe "deprecated heartbeat functions" do | ||
test "forwards heartbeat/1 to CheckIn.cron/1" do | ||
Appsignal.heartbeat("heartbeat-name") | ||
|
||
assert [ | ||
%Event{identifier: "heartbeat-name", kind: :finish, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
end | ||
|
||
test "forwards heartbeat/2 to CheckIn.cron/2" do | ||
output = Appsignal.heartbeat("heartbeat-name", fn -> "output" end) | ||
|
||
assert [ | ||
%Event{identifier: "heartbeat-name", kind: :start, check_in_type: :cron}, | ||
%Event{identifier: "heartbeat-name", kind: :finish, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
|
||
assert "output" == output | ||
end | ||
|
||
test "forwards new/1, start/1 and finish/1 to the CheckIn.Cron module" do | ||
heartbeat = Appsignal.Heartbeat.new("heartbeat-name") | ||
Appsignal.Heartbeat.start(heartbeat) | ||
Appsignal.Heartbeat.finish(heartbeat) | ||
|
||
assert [ | ||
%Event{identifier: "heartbeat-name", kind: :start, check_in_type: :cron}, | ||
%Event{identifier: "heartbeat-name", kind: :finish, check_in_type: :cron} | ||
] = FakeTransmitter.transmitted_payloads() | ||
end | ||
end | ||
end |
Oops, something went wrong.