Skip to content

Commit 2c1d07b

Browse files
authored
Documentation (#22)
Add code documentation, installation and usage guides. Closes #17
1 parent 5729824 commit 2c1d07b

File tree

26 files changed

+517
-108
lines changed

26 files changed

+517
-108
lines changed

README.md

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,7 @@ En Elixir based built-in error tracking solution.
44

55
## Configuration
66

7-
Set up the repository:
8-
9-
```elixir
10-
config :error_tracker,
11-
repo: MyApp.Repo
12-
```
13-
14-
And you are ready to go!
15-
16-
By default Phoenix and Oban integrations will start registering exceptions.
17-
18-
If you want to also catch exceptions before your Phoenix Router (in plugs used
19-
on your Endpoint) or your application just use `Plug` but not `Phoenix`, you can
20-
attach to those errors with:
21-
22-
```elixir
23-
defmodule MyApp.Endpoint do
24-
use ErrorTracker.Integrations.Plug
25-
end
26-
```
7+
Take a look at the [Getting Started](/guides/Getting%20Started.md) guide.
278

289
## Development
2910

@@ -61,5 +42,3 @@ To do so you can execute this task in a separate terminal:
6142
```
6243
mix assets.watch
6344
```
64-
65-

dev.exs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Application.put_env(:error_tracker, ErrorTrackerDevWeb.Endpoint,
4343

4444
# Setup up the ErrorTracker configuration
4545
Application.put_env(:error_tracker, :repo, ErrorTrackerDev.Repo)
46-
Application.put_env(:error_tracker, :application, :error_tracker_dev)
46+
Application.put_env(:error_tracker, :otp_app, :error_tracker_dev)
4747
Application.put_env(:error_tracker, :prefix, "private")
4848

4949
defmodule ErrorTrackerDevWeb.PageController do
@@ -54,7 +54,7 @@ defmodule ErrorTrackerDevWeb.PageController do
5454
def call(conn, :index) do
5555
content(conn, """
5656
<h2>ErrorTracker Dev Server</h2>
57-
<div><a href="/errors">Open ErrorTracker</a></div>
57+
<div><a href="/dev/errors">Open ErrorTracker</a></div>
5858
<div><a href="/plug-exception">Generate Plug exception</a></div>
5959
<div><a href="/404">Generate Router 404</a></div>
6060
<div><a href="/noroute">Raise NoRouteError from a controller</a></div>
@@ -108,7 +108,9 @@ defmodule ErrorTrackerDevWeb.Router do
108108
get "/exception", ErrorTrackerDevWeb.PageController, :exception
109109
get "/exit", ErrorTrackerDevWeb.PageController, :exit
110110

111-
error_tracker_dashboard("/errors")
111+
scope "/dev" do
112+
error_tracker_dashboard("/errors")
113+
end
112114
end
113115
end
114116

@@ -143,8 +145,8 @@ end
143145
defmodule Migration0 do
144146
use Ecto.Migration
145147

146-
def up, do: ErrorTracker.Migrations.up(prefix: "private")
147-
def down, do: ErrorTracker.Migrations.down(prefix: "private")
148+
def up, do: ErrorTracker.Migration.up(prefix: "private")
149+
def down, do: ErrorTracker.Migration.down(prefix: "private")
148150
end
149151

150152
Application.put_env(:phoenix, :serve_endpoints, true)

guides/Getting Started.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Getting Started
2+
3+
This guide is an introduction to the ErrorTracker, an Elixir based built-in error tracking solution. The ErrorTracker provides a basic and free error-tracking solution integrated in your own application. It is designed to be easy to install and easy to use so you can integrate it in your existing project with minimal changes. The only requirement is a relational database in which errors will be tracked.
4+
5+
In this guide we will learn how to install the ErrorTracker in an Elixir project so you can start reporting errors as soon as possible. We will also cover more advanced topics such as how to report custom errors and how to add extra context to reported errors.
6+
7+
**This guide requires you to have setup Ecto with PostgreSQL beforehand.**
8+
9+
## Installing the ErrorTracking as a dependency
10+
11+
The first step add the ErrorTracker to your application is to declare the package as a dependency in your `mix.exs` file:
12+
13+
```elixir
14+
# mix.exs
15+
defp deps do
16+
[
17+
{:error_tracker, "~> 1.0"}
18+
]
19+
end
20+
```
21+
22+
Once the ErrorTracker is declared as a dependency of your application, you can install it with the following command:
23+
24+
```bash
25+
mix deps.get
26+
```
27+
28+
## Configuring the ErrorTracker
29+
30+
The ErrorTracker needs a few configuration options to work. This configuration should be added in your `config/config.exs` file:
31+
32+
```elixir
33+
config :error_tracker,
34+
repo: MyApp.Repo,
35+
otp_app: :my_app
36+
```
37+
38+
The `:repo` option specifies the repository that will be used by the ErrorTracker. You can use your regular application repository, or a different one if you prefer to keep errors in a different database.
39+
40+
The `:otp_app` option specifies your application name. When an error happens the ErrorTracker will use this information to understand which parts of the stacktrace belong to your application and which parts belong to third party dependencies. This allows you to filter in-app vs third-party frames when viewing errors.
41+
42+
## Setting up the database
43+
44+
Since the ErrorTracker stores errors in the database you must create a database migration to add the required tables:
45+
46+
```
47+
mix ecto.gen.migration add_error_tracker
48+
```
49+
50+
Open the generated migration and call the `up` and `down` functions on `ErrorTracker.Migration`:
51+
52+
```elixir
53+
defmodule MyApp.Repo.Migrations.AddErrorTracker do
54+
use Ecto.Migration
55+
56+
def up, do: ErrorTracker.Migration.up()
57+
def down, do: ErrorTracker.Migration.down()
58+
end
59+
```
60+
61+
You can run the migration and perform the database changes with the following command:
62+
63+
```bash
64+
mix ecto.migrate
65+
```
66+
67+
For more information about how to handle migrations take a look at the `ErrorTracker.Migration` module docs.
68+
69+
## Automatic error tracking
70+
71+
At this point, the ErrorTracker is ready to track errors. It will automatically start when your application boots and track errors that happen in your Phoenix controllers, Phoenix LiveViews and Oban jobs. The `ErrorTracker.Integrations.Phoenix` and `ErrorTracker.Integrations.Oban` provide detailed information about how this works.
72+
73+
If your application uses Plug but not Phoenix, you will need to add the relevant integration in your `Plug.Builder` or `Plug.Router` module.
74+
75+
```elixir
76+
defmodule MyApp.Router do
77+
use Plug.Router
78+
use ErrorTracker.Integrations.Plug
79+
80+
# Your code here
81+
end
82+
```
83+
84+
This is also required if you want to track errors that happen in your Phoenix endpoint, before the Phoenix router starts handling the request. Keep in mind that this won't be needed in most cases as endpoint errors are very infrequent.
85+
86+
```elixir
87+
defmodule MyApp.Endpoint do
88+
use Phoenix.Endpoint
89+
use ErrorTracker.Integrations.Plug
90+
91+
# Your code here
92+
end
93+
```
94+
95+
You can learn more about this in the `ErrorTracker.Integrations.Plug` module documentation.
96+
97+
## Error context
98+
99+
The default integrations include some additional context when tracking errors. You can take a look at the relevant integration modules to see what is being tracked out of the box.
100+
101+
In certain cases you may want to include some additional information when tracking errors. For example it may be useful to track the user ID that was using the application when an error happened. Fortunately, the ErrorTracker allows you to enrich the default context with custom information.
102+
103+
The `ErrorTracker.set_context/1` function stores the given context in the current process so any errors that happen in that process (for example a Phoenix request or an Oban job) will include this given context along with the default integration context.
104+
105+
```elixir
106+
ErrorTracker.set_context(%{user_id: conn.assigns.current_user.id})
107+
```
108+
109+
## Manual error tracking
110+
111+
If you want to report custom errors that fall outside the default integrations scope you may use `ErrorTracker.report/2`. This allows you to report an exception by yourself:
112+
113+
```elixir
114+
try do
115+
# your code
116+
catch
117+
e ->
118+
ErrorTracker.report(e, __STACKTRACE__)
119+
end
120+
```
121+
122+
You can also use `ErrorTracker.report/3` and set some custom context that will be included along with the reported error.
123+
124+
## Web UI
125+
126+
The ErrorTracker also provides a dashboard built with Phoenix LiveView that can be used to see and manage the recorded errors.
127+
128+
This is completely optional and you can find more information about it in the `ErrorTracker.Web` module documentation.

lib/error_tracker.ex

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,61 @@
11
defmodule ErrorTracker do
22
@moduledoc """
33
En Elixir based built-in error tracking solution.
4+
5+
The main objectives behind this project are:
6+
7+
* Provide a basic free error tracking solution: because tracking errors on
8+
your application should be a requirement to almost any project, and helps to
9+
provide quality and maintenance to your project.
10+
11+
* Be easy to use: by providing plug and play integrations, documentation and a
12+
simple UI to manage your errors.
13+
14+
* Be as minimalistic as possible: you just need a database to store errors and
15+
an Phoenix application if you want to inspect them via web. That's all.
16+
17+
## Requirements
18+
19+
ErrorTracker requires Elixir 1.15+, Ecto 3.11+, Phoenix LiveView 0.19+ and PostgreSQL
20+
21+
## Integrations
22+
23+
We currently include integrations for what we consider the basic stack of
24+
an application: Phoenix, Plug and Oban.
25+
26+
However, we may continue working in adding support for more systems and
27+
libraries in the future if there is enough interest by the community.
28+
29+
If you want to manually report an error you can use the `ErrorTracker.report/3` function.
30+
31+
## Context
32+
33+
Aside from the information abot each exception (kind, message, stacktrace...)
34+
we also store contexts.
35+
36+
Contexts are arbitrary maps that allow you to store extra information of an
37+
exception to be able to reproduce it later.
38+
39+
Each integration includes a default context with the useful information they
40+
can gather, but aside from that you can also add your own information. You can
41+
do this in a per-process way or in a per-call way (or both).
42+
43+
**Per process**
44+
45+
This allows you to set general context for the current process such as a Phoenix
46+
request or an Oban job. For example you could include the following code in your
47+
authentication Plug to automatically include the user ID on any error that is
48+
tracked during the Phoenix request handling.
49+
50+
```elixir
51+
ErrorTracker.set_context(%{user_id: conn.assigns.current_user.id})
52+
```
53+
54+
**Per call**
55+
56+
As we had seen before you can use `ErrorTracker.report/3` to manually report an
57+
error. The third parameter of this function is optional and allows you to include
58+
extra context that will be tracked along with that error.
459
"""
560

661
@typedoc """
@@ -11,6 +66,40 @@ defmodule ErrorTracker do
1166
alias ErrorTracker.Error
1267
alias ErrorTracker.Repo
1368

69+
@doc """
70+
Report a exception to be stored.
71+
72+
Aside from the exception, it is expected to receive the stacktrace and,
73+
optionally, a context map which will be merged with the current process
74+
context.
75+
76+
Keep in mind that errors that happen in Phoenix controllers, Phoenix live views
77+
and Oban jobs are automatically reported. You will need this function only if you
78+
want to report custom errors.
79+
80+
```elixir
81+
try do
82+
# your code
83+
catch
84+
e ->
85+
ErrorTracker.report(e, __STACKTRACE__)
86+
end
87+
```
88+
89+
## Exceptions
90+
91+
Exceptions passed can be in three different forms:
92+
93+
* An exception struct: the module of the exception is stored alongside with
94+
the exception message.
95+
96+
* A `{kind, exception}` tuple in which the `exception` is an struct: it
97+
behaves the same as when passing just the exception struct.
98+
99+
* A `{kind, reason}` tuple: it stores the kind and the message itself casted
100+
to strings, as it is useful for some errors like EXIT signals or custom error
101+
messages.
102+
"""
14103
def report(exception, stacktrace, given_context \\ %{}) do
15104
{kind, reason} =
16105
case exception do
@@ -40,18 +129,42 @@ defmodule ErrorTracker do
40129
|> Repo.insert!()
41130
end
42131

132+
@doc """
133+
Marks an error as resolved.
134+
135+
If an error is marked as resolved and it happens again, it will automatically
136+
appear as unresolved again.
137+
"""
43138
def resolve(error = %Error{status: :unresolved}) do
44139
changeset = Ecto.Changeset.change(error, status: :resolved)
45140

46141
Repo.update(changeset)
47142
end
48143

144+
@doc """
145+
Marks an error as unresolved.
146+
"""
49147
def unresolve(error = %Error{status: :resolved}) do
50148
changeset = Ecto.Changeset.change(error, status: :unresolved)
51149

52150
Repo.update(changeset)
53151
end
54152

153+
@doc """
154+
Sets current process context.
155+
156+
By default it will merge the current context with the new one received, taking
157+
preference the new context's contents over the existsing ones if any key
158+
matches.
159+
160+
## Depth of the context
161+
162+
You can store context on more than one level of depth, but take into account
163+
that the merge operation is performed on the first level.
164+
165+
That means that any existing data on deep levels fot he current context will
166+
be replaced if the first level key is received on the new contents.
167+
"""
55168
@spec set_context(context()) :: context()
56169
def set_context(params) when is_map(params) do
57170
current_context = Process.get(:error_tracker_context, %{})
@@ -61,6 +174,9 @@ defmodule ErrorTracker do
61174
params
62175
end
63176

177+
@doc """
178+
Obtain the context of the current process.
179+
"""
64180
@spec get_context() :: context()
65181
def get_context do
66182
Process.get(:error_tracker_context, %{})

0 commit comments

Comments
 (0)