Packages
raygun
0.0.3
0.4.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.0.45
0.0.44
0.0.43
0.0.42
0.0.41
0.0.40
0.0.39
0.0.38
0.0.37
0.0.36
0.0.35
0.0.34
0.0.33
0.0.32
0.0.31
0.0.30
0.0.29
0.0.28
0.0.27
0.0.26
0.0.25
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
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
Send errors in your application to Raygun. Raygun captures all your application errors in one place. It can be used as a Plug, via Logger and/or programmatically.
Current section
Files
Jump to
Current section
Files
lib/raygun.ex
defmodule Raygun.Log.Error do
defexception message: "default message"
end
defmodule Raygun.Plug do
defmacro __using__(_env) do
quote location: :keep do
@before_compile Raygun.Plug
end
end
defmacro __before_compile__(_env) do
quote location: :keep do
defoverridable [call: 2]
def call(conn, opts) do
try do
super(conn, opts)
rescue
exception ->
Raygun.capture(exception, env: Atom.to_string(Mix.env))
reraise exception, stacktrace
end
end
end
end
end
defmodule Raygun do
use GenEvent
@api_endpoint "https://api.raygun.io"
# LOGGING SECTION
def handle_call({:configure, _options}, state) do
{:ok, :ok, state}
end
def handle_event({:error, gl, {Logger, msg, _ts, _md}}, state) when node(gl) == node() do
IO.puts "we are going to send an error to Raygun, yeah!"
if Exception.exception? msg do
capture msg
else
report_message(msg)
end
{:ok, state}
end
def handle_event(data, state) do
IO.puts "handling a logging event..."
IO.inspect data
{:ok, state}
end
# GENERAL CAPTURE SECTION
def capture(exception, opts \\ %{}) do
report(System.stacktrace, exception, opts)
end
def report_message(msg, opts \\ %{}) do
%{
occurredOn: now,
details:
details
|> Dict.merge( environment )
|> Dict.merge( user )
|> Dict.merge( custom(opts) )
|> Dict.merge( %{error: %{ message: msg } } )
}
|> Poison.encode!
|> send_report
end
def report(stacktrace, exception, opts \\ %{}) do
%{
occurredOn: now,
details:
details
|> Dict.merge( err(stacktrace, exception) )
|> Dict.merge( environment )
|> Dict.merge( user )
|> Dict.merge( custom(opts) )
}
|> Poison.encode!
|> send_report
end
def plug_report(conn, stacktrace, exception, opts \\ %{}) do
%{
occurredOn: now,
details:
details
|> Dict.merge( err(stacktrace, exception) )
|> Dict.merge( environment )
|> Dict.merge( request(conn) )
|> Dict.merge( response(conn) )
|> Dict.merge( user(conn) )
|> Dict.merge( custom(opts) )
}
|> Poison.encode!
|> send_report
end
def send_report(json) do
headers = %{
"Content-Type": "application/json; charset=utf-8",
"Accept": "application/json",
"User-Agent": "Elixir Client",
"X-ApiKey": Application.get_env(:raygun, :api_key)
}
{:ok, response} = HTTPoison.post(@api_endpoint <> "/entries", json, headers)
%HTTPoison.Response{status_code: 202} = response
end
def custom(opts) do
%{
tags: Application.get_env(:raygun,:tags),
userCustomData: opts
}
end
def user() do
%{user: Application.get_env(:raygun, :system_user)}
end
def user(_conn) do
%{user: %{
identifier: "",
isAnonymous: true,
email: "",
fullName: "",
firstName: "",
uuid: ""
}
}
end
def environment do
:disksup.start_link
disks = :disksup.get_disk_data
disk_free_spaces = for {_mount_point, capacity, percent_used} <- disks do
((100-percent_used)/100) * capacity
end
{:ok, hostname} = :inet.gethostname
hostname = hostname |> List.to_string
{os_type, os_flavor} = :os.type
os_version = "#{os_type} - #{os_flavor}"
architecture = :erlang.system_info(:system_architecture) |> List.to_string
sys_version = :erlang.system_info(:system_version) |> List.to_string
processor_count = :erlang.system_info(:logical_processors_online)
memory_used = :erlang.memory(:total)
%{environment: %{
osVersion: os_version,
architecture: architecture,
packageVersion: sys_version,
processorCount: processor_count,
totalPhysicalMemory: memory_used,
deviceName: hostname,
diskSpaceFree: disk_free_spaces,
}
}
end
def details do
{:ok, hostname} = :inet.gethostname
hostname = hostname |> List.to_string
%{
machineName: hostname,
version: Mix.Project.config[:deps][:poison],
client: %{
name: Mix.Project.config[:app],
version: Mix.Project.config[:version],
clientUrl: Mix.Project.config[:raygun][:url]
}
}
end
def now do
{:ok, datetime} = Timex.Date.now |> Timex.DateFormat.format("{ISOz}")
datetime
end
def request(conn) do
%{request: %{
hostName: conn.host,
url: Atom.to_string(conn.scheme) <> "://" <> conn.host <> ":" <> conn.port <> conn.request_path,
httpMethod: conn.method,
iPAddress: conn.remote_ip,
queryString: Plug.Conn.fetch_query_params(conn),
form: Plug.Parsers.call(conn).params,
headers: conn.req_headers,
rawData: %{}
}
}
end
def response(conn) do
%{response: %{
statusCode: conn.status
}
}
end
def err(stacktrace, error) do
s0 = Enum.at(stacktrace, 0) |> stacktrace_entry
%{error: %{
innerError: nil,
data: %{fileName: s0.fileName, lineNumber: s0.lineNumber, function: s0.methodName},
className: s0.className,
message: Exception.message(error),
stackTrace: stacktrace(stacktrace)
}
}
end
def stacktrace(s) do
s |> Enum.map(&stacktrace_entry/1)
end
def stacktrace_entry(entry = {module, function, arity_or_args, location}) do
IO.inspect entry
%{
lineNumber: line_from(location),
className: mod_for(module),
fileName: file_from(location),
methodName: fa(function,arity_or_args)
}
end
def stacktrace_entry({function, arity_or_args, location}) do
stacktrace_entry {__MODULE__, function, arity_or_args, location}
end
def fa(function,arity) do
"#{Atom.to_string(function)}/#{arity}"
end
def mod_for(module) when is_atom(module) do
Atom.to_string(module)
end
def mod_for(module) when is_binary(module) do
module
end
def line_from(file: _file, line: line) do
line
end
def file_from(file: file, line: _line) do
file |> List.to_string
end
end