My debugging experience when working in the Elixir codebase
When developing application, sometimes we need to debug the code we have written. In this article, i will share my debugging experience when working in Elixir codebase, especially when working with Elixir and Phoenix web framework.
My Elixir and Phoenix versions :
- Phoenix ~>
1.6.15
- Elixir ~>
1.12
🗒 : I’m still new in the Elixir ecosystem, I wrote this article based on the lessons I learned when I started working with the phoenix web framework.
Before We Start : Use Case
I’ve created a Phoenix app with basic features (ex : login
, register
, logout
). In this article we’ll try to debug registration flow
. The purpose of this debugging is to see the submitted data from the register page when filling out the form and clicking the REGISTER
button.
1 - IO.puts
This includes integers
, strings
and atoms
. You can use string interpolation in IO.puts
with #{}
. For example, IO.puts “user email is #{user_params[“email”]}”
. Read more here
Add some codes :
def create(conn, %{"user" => user_params}) do
IO.puts "========= My debug here"
IO.puts user_params["email"]
IO.puts "user email is #{user_params["email"]}"
...
end
After click REGISTER
button, the log will be printed.
2 - IO.inspect
Print all data structures, including lists
, structs
, tuples
, and maps
. Returns all variable names and their values. We can add inspect to print user_params
with map
type . Read more here
Add code for print user_params
:
def create(conn, %{"user" => user_params}) do
IO.puts "========= My debug here"
IO.puts user_params["email"]
IO.puts "user email is #{user_params["email"]}"
IO.inspect user_params
...
end
Using IO.inspect,
we can print user_params
with the map
type, if we use IO.puts
to handle this, the app will throw an error.
We can replace IO.puts
with IO.inspect
and the code still run as expected.
3 - dbg
dbg
is similar to IO.inspect/2
, but it also prints the code and location. We can use dbg
with interactive elixir.
3.1 - dbg without argument
def create(conn, %{"user" => user_params}) do
dbg()
...
end
the output will be like this, all will be printed from current context. If we look at this code, it will print conn
and user_params
with the current elixir location ([lib/.../xxx.ex:15: xxx.create/2]
).
[info] POST /users/register
[debug] Processing with TulisWeb.UserRegistrationController.create/2
Parameters: %{"_csrf_token" => "Ax8lCC0aARAyKwB6bkokBEEcNgkFMgcpiijcdwBzsNJC_yScvFboSm4M", "user" => %{"email" => "myemail@phoenix.com", "password" => "[FILTERED]"}}
Pipelines: [:browser, :redirect_if_user_is_authenticated]
[lib/tulis_web/controllers/user_registration_controller.ex:15: TulisWeb.UserRegistrationController.create/2]
binding() #=> [
conn: %Plug.Conn{
adapter: {Plug.Cowboy.Conn, :...},
assigns: %{current_user: nil},
body_params: %{},
cookies: %{},
......
},
user_params: %{"email" => "myemail@phoenix.com", "password" => "wowpassword"}
]
3.2 - dbg with argument
If we need to specify a data to print, we can pass an argument to dbg()
.
def create(conn, %{"user" => user_params}) do
dbg(user_params)
...
end
The output will like this,
3.3 - dbg with interactive elixir (iex)
With interactive elixir, we can pry
our dbg()
. But, we need to run Phoenix server using this command.
iex -S mix phx.server
Click the Register button again and the terminal will look like this.
We can now interact with and execute our code after pressing Y
. For example :
This is useful, instead of using IO.puts
and IO.inspect
we can use this for better debugging if there are any special cases.
Recaps — TL:DR
We can debug elixir code in several ways.
- IO.puts
- IO.inspect
- dbg with argument
- dbg without argument
- dbg with interactive elixir
You can read this official blog for more references. Thanks for reading 📚