Current section
Files
Jump to
Current section
Files
lib/ex_rollbar.ex
defmodule ExRollbar do
@moduledoc """
Error handling for RollBar.
"""
alias ExRollbar.RestAPI
alias RestAPI.Payload.Data
alias Data.Body
@doc """
Sets up ExRollbar
args must contain at least these keys:
`access_token`
`environment`
args may also contain:
* Person Object
* `person_id`, (Required to use the Person object)
* `person_email`
* `person_username`
* Custom Object
* `custom` a (jsonable) map of custom data.
* Logger
* `enable_logger` (boolean) enable logger backend
* Framework
* `framework` IE "Nerves"
* Code Version
* `code_version` a git sha or similar.
"""
@spec setup(Keyword.t) :: :ok | {:error, term}
def setup(args) do
# Token
token = Keyword.get(args, :access_token) || raise ArgumentError, "Please supply a token when setting up ExRollbar"
Application.put_env(:ex_rollbar, :access_token, token)
# Environment
env = Keyword.get(args, :environment) || raise ArgumentError, "Please supply an environment when setting up ExRollbar"
Application.put_env(:ex_rollbar, :environment, env)
# Custom
custom = Keyword.get(args, :custom, %{})
Application.put_env(:ex_rollbar, :custom, custom)
code_version = Keyword.get(args, :code_version, %{})
Application.put_env(:ex_rollbar, :code_version, code_version)
framework = Keyword.get(args, :framework)
Application.put_env(:ex_rollbar, :framework, framework)
# Person
person_id = Keyword.get(args, :person_id)
if person_id do
Application.put_env(:ex_rollbar, :person_id, person_id)
person_email = Keyword.get(args, :person_email)
person_username = Keyword.get(args, :person_username)
Application.put_env(:ex_rollbar, :person_email, person_email)
Application.put_env(:ex_rollbar, :person_username, person_username)
end
# Logger
if Keyword.get(args, :enable_logger, false) do
Logger.add_backend(Logger.Backends.ExRollbar)
end
:ok
end
@doc false
defdelegate uuid, to: UUID, as: :generate
@doc """
Reports an item to RollBar.
* `level` can be one of :error, :warn, :info, :debug
* `value` can be a Exception struct or a string
* `stack_trace` is the result of `System.stack_trace()`
* `meta` is a bucket of extra key/value data.
## Meta
Can contain any of:
* `uuid` - a uuid (generated automatically)
* `timstamp` - a unix timestamp (generated automatically)
* `context` - a context object.
* `custom` - any jsonable map of extra data
* `request_meta` - A request object from the rollbar docs
* `server_meta` - A server object from the rollbar docs
* `client_meta` - A client object from the rollbar docs
"""
@spec report(Data.level, Exception.t | binary, Exception.stack_trace, Keyword.t) :: no_return
def report(level, value, stack_trace, meta \\ []) do
if from_env(:token) do
level = parse_level(level)
%Data{
# will change frequently
level: level,
# Should exist, but could be user provided.
uuid: Keyword.get(meta, :uuid, uuid()),
timestamp: Keyword.get(meta, :timestamp, :os.system_time(:seconds)),
context: Keyword.get(meta, :context),
custom: get_custom(meta),
# These should not change at runtime.
environment: from_env(:environment),
code_version: from_env(:code_version),
framework: from_env(:framework),
# Body.
body: generate_body(value, stack_trace, meta),
person: generate_person(),
request: generate_request(Keyword.get(meta, :request_meta)),
server: generate_server(Keyword.get(meta, :server_meta)),
client: generate_client(Keyword.get(meta, :client_meta))
}
|> RestAPI.emit()
else
:ok
end
end
defp get_custom(meta) do
env_custom = from_env(:custom, %{})
meta_custom = Keyword.get(meta, :custom, %{})
Map.merge(env_custom, meta_custom)
end
defp parse_level(_), do: :error
defp generate_body(value, stack_trace, metadata)
defp generate_body(%{message: message}, stack_trace, meta),
do: generate_body(message, stack_trace, meta)
defp generate_body(value, [stack_item | _] = stack_trace, meta) do
case stack_item do
{module, _function, _arity, _file_and_line} ->
%{
trace: %Body.Trace{
frames: Enum.map(stack_trace, fn(item) ->
{item_module, item_function, _item_arity, item_file_and_line} = item
%Body.Trace.Frame{
filename: fn() ->
# FIXME
thing = Keyword.get(item_file_and_line, :file)
if thing do
thing |> to_string()
else
raise(ArgumentError, "Could not find filename for: #{inspect value}")
end
end.(),
lineno: Keyword.get(item_file_and_line, :line),
method: item_function,
class_name: item_module
}
end),
exception: %Body.Trace.Exception{
class: module,
message: value,
description: Keyword.get(meta, :description)
}
}
}
_ -> generate_body(value, [], meta)
end
end
defp generate_body(value, [], _m), do: %{message: %Body.Message{body: value}}
@spec generate_person() :: map | nil
defp generate_person() do
id = from_env(:person_id)
if id do
%Data.Person{
id: id,
username: from_env(:person_username),
email: from_env(:person_email)
}
else
nil
end
end
@spec generate_request(nil | Keyword.t) :: map
defp generate_request(nil), do: nil
defp generate_request(_meta) do
#TODO(Connor) Fixme
%{}
end
@spec generate_server(nil | Keyword.t) :: map
defp generate_server(nil), do: nil
defp generate_server(_meta) do
%{}
end
@spec generate_client(nil | Keyword.t) :: map
defp generate_client(nil), do: nil
defp generate_client(_client) do
%{}
end
@spec from_env(atom, any) :: any
def from_env(key, default \\ nil) do
Application.get_env(:ex_rollbar, key, default)
end
end