While Elixir provides built-in libraries (like HTTPoison) for making HTTP requests, the Tesla library has gained popularity due to its simplicity, ease of use, and extensibility. Tesla offers a clean and composable way to define requests, handle responses, and customize client behavior.
Even so, whenever you're working with external APIs, errors are inevitable. Network issues, server downtime, rate limiting, and unexpected responses can all lead to errors in your application. As such, it’s great to have a dependable error tracking mechanism in place to quickly identify, diagnose, and resolve issues.
AppSignal is a powerful application monitoring and error tracking solution that seamlessly integrates with Elixir apps and libraries such as Tesla.
In this article, we'll explore how to effectively track and handle Tesla errors in your app using AppSignal. We'll start by understanding the basics of Tesla and its key features. Then we'll set up a simple weather forecasting application that integrates Tesla with AppSignal. Along the way, we'll discuss common error scenarios, implement error handling techniques, and showcase how to leverage AppSignal's intuitive dashboard to monitor and analyze errors.
Pre-requisites
First, ensure you have the following in place:
- An AppSignal account. Don't have one? Sign up for a free trial.
- A WeatherAPI account to fetch weather data.
- Some experience working with Elixir and the Phoenix framework.
- An Elixir/Phoenix application to work with. You can fork the weather forecast app which we'll use throughout this tutorial.
And with that, let's get started!
Brief Overview of Tesla for Elixir
Tesla is a modular Elixir HTTP client library that you can use to build HTTP clients with flexible middleware configurations. This middleware configuration allows you to extend the behavior of your HTTP client, with features such as:
- Authentication - With Tesla, you can configure basic or token-based authentication schemes, or even define your own custom scheme.
- Error handling - You can configure the middleware to intercept request errors.
- Caching and rate limits - You can set up custom caching rules and API call rate limits to reduce the number of API calls your client makes.
Introducing Our Weather App Built with Phoenix
The application we'll use to demonstrate Tesla's features is a simple weather forecasting app built with Phoenix. It will feature a home page with a form input where a user can enter the name of a city. Upon the form submission, the user is redirected to another page that will display a three-day weather forecast. The weather forecast data will be fetched from the Weather API service using a Tesla HTTP client.
Although we won't go through a step-by-step walk-through of building the weather forecast Phoenix app, I will highlight some of the app's key features, especially how Tesla fits in and where some potential issues could come from.
Below is the summarized shell output of the tree
command on the lib
directory, which contains the most important parts of the app for our discussion:
- API client - The API client found in the
lib/weather_forecast/weather_api.ex
file uses Tesla to make and parse HTTP requests from the weatherapi.com API. - Weather controller - Contains two actions,
index
andforecast
. Theindex
action renders a form input for entering a city name. Theforecast
action takes the city parameter and calls the API client to fetch the weather forecast, which responds with the weather forecast data or an error. - The views - The index view displays the form input where a user will enter a city name and be redirected to the forecast view upon form submission. There, a three-day weather forecast or an error will be displayed.
Over the coming sections, we'll use this app to instrument Tesla API requests, errors, and more. For now, let's see how we can add AppSignal and Tesla to the app.
Adding AppSignal and Tesla to the Weather App
Open up the mix.exs
file and add the lines shown below:
You might be wondering what Hackney is and how it fits in here. While Tesla is a great choice for wrapping HTTP requests intuitively, an adapter is still needed to handle the actual job of opening connections, sending requests, parsing responses, and so forth, which is what Hackney does. That said, Hackney isn't the only adapter; there are other adapters like Finch and Gun that you can use.
With that out of the way, run mix deps.get
to fetch the packages, then mix appsignal.install <YOUR APPSIGNAL API KEY>
to run the AppSignal interactive package installer, which will ask you to choose:
- The name of your application
- The configuration method (an environment variable or a configuration file).
You'll get an output similar to the one shown below if the process runs without a hitch.
Once done, you should have AppSignal installed, and your app will start sending data to the AppSignal dashboard.
Configuring Tesla
However, we need to add a few configurations to Tesla and Hackney to complete the installation.
To begin with, add the Tesla.Middleware.Telemetry
which plugs into the app's built-in telemetry. This needs to be the first middleware in the API client, as shown below:
But before we continue, it's important to highlight how this use of middleware makes Tesla unique among Elixir HTTP clients.
The Concept of “Composable Middleware” in Tesla
Tesla uses the concept of "composable middleware" similar to the Plug router, which means:
- With Tesla, you can define a middleware chain where each middleware is executed in the order it appears for each HTTP request. For example, the
Tesla.Middleware.Telemetry
middleware is first in the chain in the API client and, as such, will be executed first. - It's heavily inspired by
Plug
, which makes it easy to use different middleware as you wish. - You have access to several built-in middleware, such as the JSON middleware, the Logger middleware, and others. At the same time, you can define your own custom middleware to implement custom logic on requests.
It's crucial to execute the Tesla.Middleware.Telemetry
middleware first, to ensure that:
- It records the start of an event before another middleware processes that event.
- It captures an event as it is before another middleware processes that event.
Let's now complete the configuration by adding the Hackney configuration to dev.exs
:
With everything configured, fire up the app and test it out a bit. You should start to see request data on your AppSignal dashboard, as shown below:
Everything looks great so far! Let's now use AppSignal's powerful custom instrumentation features to get more information on Tesla's requests.
Instrumenting Tesla with AppSignal
With the custom instrumentation provided by AppSignal, we can add informational tags to Tesla's requests to give us more insight into what is actually going on under the hood.
Modify the API client's get_forecast/1
function as shown below:
An AppSignal.instrument/2-3
function wraps the code where we define it in a span. In the case of the weather forecast app, this is the get_forecast/1
function. When this code is executed, the span captures useful information such as execution time and any errors that might occur.
The screenshots below show how such instrumented requests are captured on the AppSignal dashboard:
And if you click on the link shown, you get even more information on the request:
Tesla Errors
Whenever you work with an HTTP client library like Tesla, you can expect some of the errors listed below:
- Rate-limiting errors
- Authentication errors
- Network errors
- Response errors
And more.
Let's pick one of these and show how to custom instrument it using AppSignal.
Instrumenting Tesla Authentication Errors with AppSignal
Different APIs will require different authentication mechanisms. For the WeatherAPI.com API, authentication is accomplished through an API key.
To simulate an authentication error, I'm going to regenerate the API key, but still use the old API key with the weather forecast app. Then I'll modify the API client's get_forecast/1
function, as shown below:
Here's a breakdown of what's going on:
- We first wrap the
get_forecast/1
function withAppSignal.instrument/2
, then define a new span usingAppSignal.Span.set_sample_data/3
, with the city name as the sample data. - A request is then made to the weather forecast API. If an error occurs (in this case, the authentication error that we expect), the error is created with all the information we need and then forwarded to the app's AppSignal dashboard.
- The private
get_application_stacktrace
function gets the full stack trace of the current process. Since this can include a bunch of entries from other Phoenix/Elixir modules, we filter for those that contain the string "WeatherForecast" as this is what we are really interested in. This filtered stack trace is what gets sent to AppSignal.
You can see the dashboard results below:
As well as the error details:
And, finally, the stack trace sample:
And that's that!
Wrapping Up
In this article, we explored how the Elixir HTTP client library Tesla works.
We learned how to install and use Tesla in a Phoenix application and then installed AppSignal to instrument Tesla requests. We custom-instrumented requests to dig deeper into stack traces and the time taken for API calls. With this information, you are now better equipped to build more effective performance profiling for your Elixir apps.
Happy coding!
P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, subscribe to our Elixir Alchemy newsletter and never miss a single post!