Skip to content

Commit

Permalink
finito
Browse files Browse the repository at this point in the history
  • Loading branch information
marcdel committed May 12, 2023
1 parent ebee0dc commit cd4e15a
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 11 deletions.
216 changes: 205 additions & 11 deletions content/en/docs/instrumentation/erlang/getting-started/phoenix.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ title: Phoenix
weight: 3
---

This page will show you how to get started with OpenTelemetry in the Phoenix Web
Framework.
This guide will show you how to get started with OpenTelemetry in the Phoenix
Web Framework.

You will learn how you can instrument a simple application automatically, in
such a way that [traces][] are emitted to the console.
such a way that traces are emitted to the console.

## Prerequisites

Expand All @@ -28,11 +28,15 @@ To begin, use the `phx` generator to create a new project
mix phx.new your_app_name
```

Press `y` when asked to fetch and install dependencies.
Press `n` when asked to fetch and install dependencies, we'll do that after the
next step.

ℹ️ Throughout this guide I will use `your_app_name` or `YourAppName`, be sure to
replace these with your actual application's name.

### Dependencies

You'll need a few other dependencies that Phoenix doesn't come with as well.
We'll need a few other dependencies that Phoenix doesn't come with as well.

- `opentelemetry_api`: contains the interfaces you'll use to instrument your
code. Things like `Tracer.with_span` and `Tracer.set_attribute` are defined
Expand All @@ -41,11 +45,11 @@ You'll need a few other dependencies that Phoenix doesn't come with as well.
the API. Without it, all the functions in the API are no-ops.
- `opentelemetry_exporter`: allows you to send your telemetry data to an
OpenTelemetry Collector and/or to self-hosted or commercial services.
- `opentelemetry_ecto` (optional): creates OpenTelemetry spans from the Elixir
`:telemetry` events created by Ecto.
- `opentelemetry_phoenix` (optional): creates OpenTelemetry spans from the
Elixir `:telemetry` events created by Phoenix.
- `opentelemetry_cowboy` (optional): creates OpenTelemetry spans from the Elixir
- `opentelemetry_ecto`: creates OpenTelemetry spans from the Elixir `:telemetry`
events created by Ecto.
- `opentelemetry_phoenix`: creates OpenTelemetry spans from the Elixir
`:telemetry` events created by Phoenix.
- `opentelemetry_cowboy`: creates OpenTelemetry spans from the Elixir
`:telemetry` events created by the Cowboy web server (which is used by
Phoenix).

Expand All @@ -56,7 +60,6 @@ def deps do
{:opentelemetry, "~> 1.3"},
{:opentelemetry_api, "~> 1.2"},
{:opentelemetry_exporter, "~> 1.4"},

{:opentelemetry_ecto, "~> 1.1"},
{:opentelemetry_phoenix, "~> 1.1"},
{:opentelemetry_cowboy, "~> 0.2"}
Expand All @@ -75,3 +78,194 @@ def start(_type, _args) do
OpentelemetryEcto.setup([:your_app_name, :repo])
end
```

We also need to configure the `opentelemetry` application as temporary by adding
a `releases` section to your project configuration. This will ensure that if it
terminates, even abnormally, no other applications will be terminated.

```elixir
# mix.exs
def project do
[
app: :your_app_name,
version: "0.1.0",
elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
releases: [
your_app_name: [
applications: [opentelemetry: :temporary]
]
],
aliases: aliases(),
deps: deps()
]
end
```

Now we can use the new `mix setup` command to install the dependencies, build
the assets, and create and migrate the database.

### Try It Out!

We can ensure everything is working by setting the stdout exporter as
opentelemetry's traces_exporter and starting the app with `mix phx.server`.

```elixir
# config/dev.exs
config :opentelemetry, traces_exporter: {:otel_exporter_stdout, []}
```

If everything went well, you should be able to visit `http://localhost:4000` in
your browser and see quite a few lines that look like this in your terminal.
Don't worry if the format looks a little unfamiliar. Spans are recorded in the
Erlang `record` data structure. You can find more information about records
[here](https://www.erlang.org/doc/reference_manual/records.html), and
[this](https://github.com/open-telemetry/opentelemetry-erlang/blob/main/apps/opentelemetry/include/otel_span.hrl#L19)
file describes the `span` record structure, and explains what all the different
fields are.

```shell
*SPANS FOR DEBUG*
{span,64480120921600870463539706779905870846,11592009751350035697,[],
undefined,<<"/">>,server,-576460731933544855,-576460731890088522,
{attributes,128,infinity,0,
#{'http.status_code' => 200,
'http.client_ip' => <<"127.0.0.1">>,
'http.flavor' => '1.1','http.method' => <<"GET">>,
'http.scheme' => <<"http">>,'http.target' => <<"/">>,
'http.user_agent' =>
<<"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36">>,
'net.transport' => 'IP.TCP',
'net.host.name' => <<"localhost">>,
'net.host.port' => 4000,'net.peer.port' => 62839,
'net.sock.host.addr' => <<"127.0.0.1">>,
'net.sock.peer.addr' => <<"127.0.0.1">>,
'http.route' => <<"/">>,'phoenix.action' => home,
'phoenix.plug' =>
'Elixir.OtelPhxSetupWeb.PageController'}},
{events,128,128,infinity,0,[]},
{links,128,128,infinity,0,[]},
undefined,1,false,
{instrumentation_scope,<<"opentelemetry_phoenix">>,<<"1.1.0">>,
undefined}}
```
These are the raw structured logs that will get sent when you configure the
exporter for your preferred service.
### Rolling The Dice
Now we'll add an API endpoint that will let us roll the dice and return a random
number between 1 and 6.
To start, we'll add a route to the `/api` scope in your router
```elixir
# lib/your_app_name_web/router.ex
scope "/api", OtelPhxSetupWeb do
pipe_through :api
get "/rolldice", DiceController, :roll
end
```
Then we'll create a new file in the controllers folder for that module. We told
the router that we will define a roll function, so we'll do that. It will return
a `200` response code and the result of a `dice_roll` function, which we will
emit a span for. We also want to set the value of the generated roll as an
attribute on the span.
```elixir
# lib/your_app_name_web/controllers/dice_controller.ex
defmodule YourAppNameWeb.DiceController do
use YourAppNameWeb, :controller
require OpenTelemetry.Tracer, as: Tracer
def roll(conn, _params) do
send_resp(conn, 200, dice_roll())
end
defp dice_roll do
Tracer.with_span("dice_roll") do
roll = Enum.random(1..6)
Tracer.set_attribute(:roll, roll)
to_string(roll)
end
end
end
```
If you point your browser/curl/etc. to `http://localhost:4000/api/rolldice` you
should get a random number in response, and a few spans in your console. I'll
point out some of the most relevant bits.
```shell
<<"/api/rolldice">>
'http.status_code' => 200
'http.method' => <<"GET">>
'http.target' => <<"/api/rolldice">>
<<"dice_roll">>
{attributes,128,infinity,0,#{roll => 3}}
```
<details>
<summary>View the full spans</summary>
```shell
{span,179794490720632128348966072237843029671,8335582746889793882,[],
undefined,<<"/api/rolldice">>,server,-576460682893356250,
-576460682892293541,
{attributes,128,infinity,0,
#{'http.status_code' => 200,
'http.client_ip' => <<"127.0.0.1">>,
'http.flavor' => '1.1','http.method' => <<"GET">>,
'http.scheme' => <<"http">>,
'http.target' => <<"/api/rolldice">>,
'http.user_agent' =>
<<"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36">>,
'net.transport' => 'IP.TCP',
'net.host.name' => <<"localhost">>,
'net.host.port' => 4000,'net.peer.port' => 65111,
'net.sock.host.addr' => <<"127.0.0.1">>,
'net.sock.peer.addr' => <<"127.0.0.1">>,
'http.route' => <<"/api/rolldice">>,
'phoenix.action' => roll,
'phoenix.plug' =>
'Elixir.OtelPhxSetupWeb.DiceController'}},
{events,128,128,infinity,0,[]},
{links,128,128,infinity,0,[]},
undefined,1,false,
{instrumentation_scope,<<"opentelemetry_phoenix">>,<<"1.1.0">>,
undefined}}
{span,179794490720632128348966072237843029671,4318919905237729135,[],
8335582746889793882,<<"dice_roll">>,internal,-576460682892357583,
-576460682892320375,
{attributes,128,infinity,0,#{roll => 3}},
{events,128,128,infinity,0,[]},
{links,128,128,infinity,0,[]},
undefined,1,false,
{instrumentation_scope,<<"otel_phx_setup">>,<<"0.1.0">>,undefined}}
```
</details>
<br/>
You will have other spans from Phoenix, Cowboy, and Ecto as well, and these will
all be linked together into a trace which can be rendered into a nice waterfall
diagram in your tracing tool of choice.
## Next Steps
Enrich your automatically generated instrumentation with
[manual instrumentation](/docs/instrumentation/erlang/instrumentation) of your
own codebase. This allows you to customize the observability data your
application emits.
You'll also want to configure an appropriate exporter to
[export your telemetry data](/docs/instrumentation/erlang/getting-started#exporting-to-the-opentelemetry-collector)
to one or more telemetry backends.
4 changes: 4 additions & 0 deletions static/refcache.json
Original file line number Diff line number Diff line change
Expand Up @@ -4099,6 +4099,10 @@
"StatusCode": 206,
"LastSeen": "2023-04-07T13:36:35.670878-04:00"
},
"https://www.erlang.org/doc/reference_manual/records.html": {
"StatusCode": 206,
"LastSeen": "2023-05-12T12:27:37.777863-07:00"
},
"https://www.eventbrite.com/e/otel-unplugged-kubeconcloudnativecon-detroit-2022-tickets-427595037267": {
"StatusCode": 200,
"LastSeen": "2023-02-18T13:43:51.052572-05:00"
Expand Down

0 comments on commit cd4e15a

Please sign in to comment.