Skip to content

Commit

Permalink
Add Honeybadger.event interface (#564)
Browse files Browse the repository at this point in the history
Fixes #554

---------

Co-authored-by: Benjamin Curtis <[email protected]>
  • Loading branch information
mdg and stympy authored Aug 29, 2024
1 parent 503842c commit d7bf1f9
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 1 deletion.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,29 @@ Honeybadger.add_breadcrumb("Email sent", metadata: %{

---

### `Honeybadger.event`: Send an event to Honeybadger Insights.

Use the `Honeybadger.event/1` function to send event data to the events API. A
`ts` field with the current timestamp will be added to the data if it isn't
provided. You can also use `Honeybadger.event/2`, which accepts a string as the
first parameter and adds that value to the `event_type` field in the map before
being sent to the API.

#### Examples:

```elixir
Honeybadger.event(%{
event_type: "user_created",
user: user.id
})

Honeybadger.event("project_deleted", %{
project: project.name
})
```

---

## Proxy configuration

If your server needs a proxy to access Honeybadger, add the following to your config
Expand Down Expand Up @@ -427,7 +450,7 @@ will be accepted.

#### Github Workflow

A new version can be published on Hex.pm using the Publish New Release workflow.
A new version can be published on Hex.pm using the Publish New Release workflow.
The workflow can be triggered manually from the Github Actions page and takes the following input:
- `version`: One of `patch`, `minor` or `major`. The version number will be bumped accordingly.
- `changes`: An entry to be added to the changelog.
Expand Down
16 changes: 16 additions & 0 deletions lib/honeybadger.ex
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,22 @@ defmodule Honeybadger do
end
end

@spec event(String.t(), map()) :: :ok
def event(event_type, event_data) when is_map(event_data) do
event_data
|> Map.put(:event_type, event_type)
|> event()
end

@spec event(map()) :: :ok
def event(event_data) do
ts = DateTime.utc_now() |> DateTime.to_string()

event_data
|> Map.put_new(:ts, ts)
|> Client.send_event()
end

@doc """
Stores a breadcrumb item.
Expand Down
56 changes: 56 additions & 0 deletions lib/honeybadger/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule Honeybadger.Client do
]
@max_connections 20
@notices_endpoint "/v1/notices"
@events_endpoint "/v1/events"

# State

Expand All @@ -24,6 +25,7 @@ defmodule Honeybadger.Client do
proxy: binary(),
proxy_auth: {binary(), binary()},
url: binary(),
event_url: binary(),
hackney_opts: keyword()
}

Expand All @@ -34,6 +36,7 @@ defmodule Honeybadger.Client do
:proxy,
:proxy_auth,
:url,
:event_url,
:hackney_opts
]

Expand All @@ -55,6 +58,7 @@ defmodule Honeybadger.Client do
proxy: get_env(opts, :proxy),
proxy_auth: get_env(opts, :proxy_auth),
url: get_env(opts, :origin) <> @notices_endpoint,
event_url: get_env(opts, :origin) <> @events_endpoint,
hackney_opts: get_env(opts, :hackney_opts)
}
end
Expand All @@ -71,6 +75,20 @@ defmodule Honeybadger.Client do
end
end

@doc """
Upload the event data
"""
@spec send_event(map) :: :ok
def send_event(event) when is_map(event) do
if pid = Process.whereis(__MODULE__) do
GenServer.cast(pid, {:event, event})
else
Logger.warning(fn ->
"[Honeybadger] Unable to post event, the :honeybadger client isn't running"
end)
end
end

@doc """
Check whether reporting is enabled for the current environment.
Expand Down Expand Up @@ -148,6 +166,44 @@ defmodule Honeybadger.Client do
{:noreply, state}
end

def handle_cast({:event, _}, %{enabled: false} = state) do
{:noreply, state}
end

def handle_cast({:event, _}, %{api_key: nil} = state) do
{:noreply, state}
end

def handle_cast(
{:event, event},
%{enabled: true, event_url: event_url, headers: headers} = state
) do
case Honeybadger.JSON.encode(event) do
{:ok, payload} ->
opts =
state
|> Map.take([:proxy, :proxy_auth])
|> Enum.into(Keyword.new())
|> Keyword.put(:pool, __MODULE__)

hackney_opts =
state
|> Map.get(:hackney_opts)
|> Keyword.merge(opts)

# post logic for events is the same as notices
post_notice(event_url, headers, payload, hackney_opts)

{:error, %Jason.EncodeError{message: message}} ->
Logger.warning(fn -> "[Honeybadger] Event encoding failed: #{message}" end)

{:error, %Protocol.UndefinedError{description: message}} ->
Logger.warning(fn -> "[Honeybadger] Event encoding failed: #{message}" end)
end

{:noreply, state}
end

@impl GenServer
def handle_info(message, state) do
Logger.info(fn -> "[Honeybadger] unexpected message: #{inspect(message)}" end)
Expand Down

0 comments on commit d7bf1f9

Please sign in to comment.