Current section
Files
Jump to
Current section
Files
lib/rollbax.ex
defmodule Rollbax do
@moduledoc """
This module provides functions to report any kind of exception or message to
[Rollbar](https://rollbar.com).
## Configuration
The `:rollbax` application needs to be configured properly in order to
work. This configuration can be done, for example, in `config/config.exs`:
config :rollbax,
access_token: "9309123491",
environment: "production"
The following is a comprehensive list of configuration options supported by Rollbax:
* `:access_token` - (binary or `nil`) the token needed to access the [Rollbar
Items API (POST)](https://rollbar.com/docs/api/items_post/). As of now, Rollbar provides
several access tokens for different "parts" of their API: for this configuration option, the
`"post_server_item"` access token is needed. This option is required only when the
`:enabled` option is set to `true`, and can be `nil` otherwise.
* `:environment` - (binary) the environment that will be attached to each reported exception.
* `:enabled` - (`true | false | :log`) decides whether things reported with `report/5` or
`report_message/4` are actually reported to Rollbar. If `true`, they are reported; if
`false`, `report/5` and `report_message/4` don't do anything; if `:log`, things reported
with `report/5` and `report_message/4` are instead logged to the shell.
* `:custom` - (map) a map of any arbitrary metadata you want to attach to everything reported
by Rollbax. If custom data is specified in an individual call to `report/5` or
`report_message/5` it will be merged with the global data, with the individual data taking
precedence in case of conflicts. Defaults to `%{}`.
* `:api_endpoint` - (binary) the Rollbar endpoint to report exceptions and messages to.
Defaults to `https://api.rollbar.com/api/1/item/`.
* `:enable_crash_reports` - see `Rollbax.Logger`.
* `:reporters` - see `Rollbax.Logger`.
## Runtime configuration
Configuration can be modified at runtime by providing a configuration callback, like this:
config :rollbax,
config_callback: {MyModule, :my_function}
In the example above, `MyModule.my_function/1` will be called with the existing configuration as
an argument. It's supposed to return a keyword list representing a possibly modified
configuration. This can for example be used to read system environment variables at runtime when
the application starts:
defmodule MyModule do
def my_function(config) do
Keyword.put(config, :access_token, System.get_env("ROLLBAR_ACCESS_TOKEN"))
end
end
## Logger backend
Rollbax provides a module that reports logged crashes and exits to Rollbar. For more
information, look at the documentation for `Rollbax.Logger`.
"""
use Application
@allowed_message_levels [:critical, :error, :warning, :info, :debug]
@doc false
def start(_type, _args) do
config = init_config()
if config[:enabled] == true and is_nil(config[:access_token]) do
raise ArgumentError, ":access_token is required when :enabled is true"
end
if config[:enable_crash_reports] do
# We do this because the handler will read `:reporters` out of the app's environment.
Application.put_env(:rollbax, :reporters, config[:reporters])
:error_logger.add_report_handler(Rollbax.Logger)
end
children = [
Supervisor.Spec.worker(Rollbax.Client, [config])
]
Supervisor.start_link(children, strategy: :one_for_one)
end
defp init_config() do
env = Application.get_all_env(:rollbax)
config =
env
|> Keyword.take([:enabled, :custom, :api_endpoint, :enable_crash_reports, :reporters])
|> put_if_present(:environment, env[:environment])
|> put_if_present(:access_token, env[:access_token])
case Application.get_env(:rollbax, :config_callback) do
{config_callback_mod, config_callback_fun} ->
apply(config_callback_mod, config_callback_fun, [config])
nil ->
config
end
end
defp put_if_present(keyword, key, value) do
if value, do: Keyword.put(keyword, key, value), else: keyword
end
@doc """
Reports the given error/exit/throw.
`kind` specifies the kind of exception being reported while `value` specifies
the value of that exception. `kind` can be:
* `:error` - reports an exception defined with `defexception`. `value` must
be an exception, or this function will raise an `ArgumentError` exception.
* `:exit` - reports an exit. `value` can be any term.
* `:throw` - reports a thrown term. `value` can be any term.
The `custom` and `occurrence_data` arguments can be used to customize metadata
sent to Rollbar. `custom` is a map of any arbitrary metadata you want to
attach to the exception being reported. `occurrence_data` is a map of
key-value pairs where keys and values should be understood by the [Rollbar
POST API for items](https://rollbar.com/docs/api/items_post/); for example, as
of now Rollbar understands the `"person"` field and uses it to display users
which an exception affected: `occurrence_data` can be used to attach
`"person"` data to an exception being reported. Refer to the Rollbar API
(linked above) for what keys are supported and what the corresponding values
should be.
This function is **fire-and-forget**: it will always return `:ok` right away and
perform the reporting of the given exception in the background so as to not block
the caller.
## Examples
Exceptions can be reported directly:
Rollbax.report(:error, ArgumentError.exception("oops"), System.stacktrace())
#=> :ok
Often, you'll want to report something you either rescued or caught. For
rescued exceptions:
try do
raise ArgumentError, "oops"
rescue
exception ->
Rollbax.report(:error, exception, System.stacktrace())
# You can also reraise the exception here with reraise/2
end
For caught exceptions:
try do
throw(:oops)
# or exit(:oops)
catch
kind, value ->
Rollbax.report(kind, value, System.stacktrace())
end
Using custom data:
Rollbax.report(:exit, :oops, System.stacktrace(), %{"weather" => "rainy"})
"""
@spec report(:error | :exit | :throw, any, [any], map, map) :: :ok
def report(kind, value, stacktrace, custom \\ %{}, occurrence_data \\ %{})
when kind in [:error, :exit, :throw] and is_list(stacktrace) and is_map(custom) and
is_map(occurrence_data) do
{class, message} = Rollbax.Item.exception_class_and_message(kind, value)
report_exception(%Rollbax.Exception{
class: class,
message: message,
stacktrace: stacktrace,
custom: custom,
occurrence_data: occurrence_data
})
end
@doc """
Reports the given `message`.
`message` will be reported as a simple Rollbar message, for example, without a stacktrace.
`level` is the level of the message, which can be one of:
* `:critical`
* `:error`
* `:warning`
* `:info`
* `:debug`
`custom` and `occurrence_data` work exactly like they do in `report/5`.
## Examples
Rollbax.report_message(:critical, "Everything is on fire!")
#=> :ok
"""
@spec report_message(:critical | :error | :warning | :info | :debug, IO.chardata(), map, map) ::
:ok
def report_message(level, message, custom \\ %{}, occurrence_data \\ %{})
when level in @allowed_message_levels and is_map(custom) and is_map(occurrence_data) do
body = message |> IO.chardata_to_string() |> Rollbax.Item.message_body()
Rollbax.Client.emit(level, System.system_time(:seconds), body, custom, occurrence_data)
end
@doc false
@spec report_exception(Rollbax.Exception.t()) :: :ok
def report_exception(%Rollbax.Exception{} = exception) do
%{
class: class,
message: message,
stacktrace: stacktrace,
custom: custom,
occurrence_data: occurrence_data
} = exception
body = Rollbax.Item.exception_body(class, message, stacktrace)
Rollbax.Client.emit(:error, System.system_time(:seconds), body, custom, occurrence_data)
end
end