Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use env variables #31

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
]
52 changes: 27 additions & 25 deletions lib/raygun.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,18 @@ defmodule Raygun do
can also be used to report any string message.
"""
def report_message(msg, opts \\ []) do
Raygun.Format.message_payload(msg, opts) |> send_report
end

@doc """
Convenience function that captures the most recent stacktrace and reports it
along with the exception. NOTE: it is the responsiblity of the caller to
ensure that the most recent stacktrace is the one associated with the
exception.
"""
def report_exception(exception, opts \\ []) do
System.stacktrace |> report_stacktrace(exception, opts)
msg
|> Raygun.Format.message_payload(opts)
|> send_report()
end

@doc """
Reports an exception and its corresponding stacktrace to Raygun.
"""
def report_stacktrace(stacktrace, exception, opts \\ []) do
Raygun.Format.stacktrace_payload(stacktrace, exception, opts) |> send_report
stacktrace
|> Raygun.Format.stacktrace_payload(exception, opts)
|> send_report()
end

@doc """
Expand All @@ -42,22 +36,30 @@ defmodule Raygun do
the exception occurred by retrieving some state from the Plug Conn.
"""
def report_plug(conn, stacktrace, exception, opts \\ []) do
Raygun.Format.conn_payload(conn, stacktrace, exception, opts) |> send_report
conn
|> Raygun.Format.conn_payload(stacktrace, exception, opts)
|> send_report()
end

defp send_report(error) do
headers = %{
"Content-Type": "application/json; charset=utf-8",
"Accept": "application/json",
"User-Agent": "Elixir Client",
"X-ApiKey": Raygun.Util.get_env(:raygun, :api_key)
}
opts = Application.get_env(:raygun, :httpoison_opts, [])
case HTTPoison.post(@api_endpoint, Poison.encode!(error), headers, opts) do
{ :ok, %HTTPoison.Response{status_code: 202} } -> :ok
{ :ok, %HTTPoison.Response{status_code: 400} } -> {:error, :bad_message}
{ :ok, %HTTPoison.Response{status_code: 403} } -> {:error, :invalid_api_key}
{ :error, _} -> {:error, :unexpected}
case HTTPoison.post(@api_endpoint, Jason.encode!(error), headers(), httpoison_opts()) do
{:ok, %HTTPoison.Response{status_code: 202}} -> :ok
{:ok, %HTTPoison.Response{status_code: 400}} -> {:error, :bad_message}
{:ok, %HTTPoison.Response{status_code: 403}} -> {:error, :invalid_api_key}
{:error, _} -> {:error, :unexpected}
end
end

defp headers do
[
{"Content-Type", "application/json; charset=utf-8"},
{"Accept", "application/json"},
{"User-Agent", "Elixir Client"},
{"X-ApiKey", Raygun.Util.get_env(:raygun, :api_key)}
]
end

defp httpoison_opts do
Application.get_env(:raygun, :httpoison_opts, [])
end
end
198 changes: 107 additions & 91 deletions lib/raygun/format.ex
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
defmodule Raygun.Format do

@moduledoc """
This module builds payloads of error messages that Raygun will understand.
These functions return maps of data which will be encoding as JSON prior
to submission to Raygun.
"""

@raygun_version Mix.Project.config[:version]
@raygun_version Mix.Project.config()[:version]

@doc """
Builds an error payload that Raygun will understand for a string.
"""
def message_payload(msg, opts) when is_list(msg) do
msg_as_string = List.to_string(msg)
message_payload(msg_as_string, opts)
msg |> List.to_string() |> message_payload(opts)
end

def message_payload(msg, opts) do
%{
occurredOn: now,
details:
details
|> Map.merge( environment )
|> Map.merge( user(opts) )
|> Map.merge( custom(opts) )
|> Map.merge( %{error: %{ message: msg } } )
"occurredOn" => now(),
"details" =>
details()
|> Map.merge(environment())
|> Map.merge(user(opts))
|> Map.merge(custom(opts))
|> Map.merge(%{"error" => %{"message" => msg}})
}
end

Expand All @@ -33,13 +32,13 @@ defmodule Raygun.Format do
"""
def stacktrace_payload(stacktrace, exception, opts) do
%{
occurredOn: now,
details:
details
|> Map.merge( err(stacktrace, exception) )
|> Map.merge( environment )
|> Map.merge( user(opts) )
|> Map.merge( custom(opts) )
"occurredOn" => now(),
"details" =>
details()
|> Map.merge(err(stacktrace, exception))
|> Map.merge(environment())
|> Map.merge(user(opts))
|> Map.merge(custom(opts))
}
end

Expand All @@ -49,15 +48,15 @@ defmodule Raygun.Format do
"""
def conn_payload(conn, stacktrace, exception, opts) do
%{
occurredOn: now,
details:
"occurredOn" => now(),
"details" =>
details(opts)
|> Map.merge( err(stacktrace, exception) )
|> Map.merge( environment )
|> Map.merge( request(conn) )
|> Map.merge( response(conn) )
|> Map.merge( user(opts) )
|> Map.merge( custom(opts) )
|> Map.merge(err(stacktrace, exception))
|> Map.merge(environment())
|> Map.merge(request(conn))
|> Map.merge(response(conn))
|> Map.merge(user(opts))
|> Map.merge(custom(opts))
}
end

Expand All @@ -67,8 +66,8 @@ defmodule Raygun.Format do
"""
def custom(opts) do
%{
tags: Raygun.Util.get_env(:raygun,:tags),
userCustomData: Enum.into(opts |> Keyword.delete(:user), %{})
"tags" => Raygun.Util.get_env(:raygun, :tags),
"userCustomData" => Enum.into(opts |> Keyword.delete(:user), %{})
}
end

Expand All @@ -77,39 +76,31 @@ defmodule Raygun.Format do
If not, it gets the system user if one is specified.
"""
def user(opts) do
if Keyword.has_key?(opts, :user) and Keyword.get(opts, :user) do
%{user: Keyword.get(opts,:user)}
else
if Raygun.Util.get_env(:raygun, :system_user) do
%{user: Raygun.Util.get_env(:raygun, :system_user)}
else
cond do
Keyword.has_key?(opts, :user) and Keyword.get(opts, :user) ->
%{"user" => Keyword.get(opts, :user)}

Raygun.Util.get_env(:raygun, :system_user) ->
%{"user" => Raygun.Util.get_env(:raygun, :system_user)}

true ->
%{}
end
end
end

@doc """
Return a map of information about the environment in which the bug was encountered.
"""
def environment do
disk_free_spaces = []

{: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,
%{
"environment" => %{
"osVersion" => os_version(),
"architecture" => List.to_string(:erlang.system_info(:system_architecture)),
"packageVersion" => List.to_string(:erlang.system_info(:system_version)),
"processorCount" => :erlang.system_info(:logical_processors_online),
"totalPhysicalMemory" => :erlang.memory(:total),
"deviceName" => device_name(),
"diskSpaceFree" => []
}
}
end
Expand All @@ -118,37 +109,33 @@ defmodule Raygun.Format do
Returns deatils about the client and server machine.
"""
def details(opts \\ []) do
{:ok, hostname} = :inet.gethostname
hostname = hostname |> List.to_string

app_version = if opts[:version] do opts[:version] else Raygun.Util.get_env(:raygun, :client_version) end

%{
machineName: hostname,
version: app_version,
client: %{
name: Raygun.Util.get_env(:raygun, :client_name),
version: @raygun_version,
clientUrl: Raygun.Util.get_env(:raygun, :url),
"machineName" => device_name(),
"version" => app_version(opts),
"client" => %{
"name" => Raygun.Util.get_env(:raygun, :client_name),
"version" => @raygun_version,
"clientUrl" => Raygun.Util.get_env(:raygun, :url)
}
}
end

defp now, do: DateTime.utc_now |> DateTime.to_iso8601
defp now, do: DateTime.to_iso8601(DateTime.utc_now())

@doc """
Given a Plug Conn return a map containing information about the request.
"""
def request(conn) do
%{request: %{
hostName: conn.host,
url: conn.request_path,
httpMethod: conn.method,
iPAddress: conn.remote_ip |> :inet.ntoa |> List.to_string,
queryString: Plug.Conn.fetch_query_params(conn).query_params,
form: Plug.Parsers.call(conn, []).params,
headers: Raygun.Util.format_headers(conn.req_headers),
rawData: %{}
%{
"request" => %{
"hostName" => conn.host,
"url" => conn.request_path,
"httpMethod" => conn.method,
"iPAddress" => conn.remote_ip |> :inet.ntoa() |> List.to_string(),
"queryString" => Plug.Conn.fetch_query_params(conn).query_params,
"form" => Plug.Parsers.call(conn, []).params,
"headers" => Raygun.Util.format_headers(conn.req_headers),
"rawData" => %{}
}
}
end
Expand All @@ -157,23 +144,32 @@ defmodule Raygun.Format do
Given a Plug Conn return a map containing information about the response.
"""
def response(conn) do
%{response: %{
statusCode: conn.status
}
}
%{"response" => %{"statusCode" => conn.status}}
end

@doc """
Given a stacktrace and an exception, return a map with the error data.
"""
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)
err(
stacktrace,
error,
stacktrace_entry(Enum.at(stacktrace, 0))
)
end

def err(stacktrace, error, line) do
%{
"error" => %{
"innerError" => nil,
"data" => %{
"fileName" => line["fileName"],
"lineNumber" => line["lineNumber"],
"function" => line["methodName"]
},
"className" => line["className"],
"message" => Exception.message(error),
"stackTrace" => stacktrace(stacktrace)
}
}
end
Expand All @@ -182,23 +178,43 @@ defmodule Raygun.Format do
Given a stacktrace return a list of maps for the frames.
"""
def stacktrace(s) do
s |> Enum.map(&stacktrace_entry/1)
Enum.map(s, &stacktrace_entry/1)
end

@doc """
Given a stacktrace frame, return a map with the information in a structure
that Raygun will understand.
"""
def stacktrace_entry({function, arity_or_args, location}) do
stacktrace_entry {__MODULE__, function, arity_or_args, location}
stacktrace_entry({__MODULE__, function, arity_or_args, location})
end

def stacktrace_entry({module, function, arity_or_args, location}) do
%{
lineNumber: Raygun.Util.line_from(location),
className: Raygun.Util.mod_for(module),
fileName: Raygun.Util.file_from(location),
methodName: Raygun.Util.function_and_arity(function,arity_or_args)
"lineNumber" => Raygun.Util.line_from(location),
"className" => Raygun.Util.mod_for(module),
"fileName" => Raygun.Util.file_from(location),
"methodName" => Raygun.Util.function_and_arity(function, arity_or_args)
}
end

defp app_version(opts) do
if opts[:version] do
opts[:version]
else
Raygun.Util.get_env(:raygun, :client_version)
end
end

defp device_name do
case :inet.gethostname() do
{:ok, hostname} -> List.to_string(hostname)
_other -> ""
end
end

defp os_version do
{os_type, os_flavor} = :os.type()
"#{os_type} - #{os_flavor}"
end
end
Loading