<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Daniel Cândido</title>
    <description>The latest articles on Forem by Daniel Cândido (@danielcandidos).</description>
    <link>https://forem.com/danielcandidos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1512645%2F7030ccf1-bf87-47ff-8305-09ed0019d3a7.png</url>
      <title>Forem: Daniel Cândido</title>
      <link>https://forem.com/danielcandidos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danielcandidos"/>
    <language>en</language>
    <item>
      <title>How to debug a Python and Django application inside a Docker container</title>
      <dc:creator>Daniel Cândido</dc:creator>
      <pubDate>Mon, 05 Aug 2024 21:35:23 +0000</pubDate>
      <link>https://forem.com/danielcandidos/how-to-debug-a-python-and-django-application-inside-a-docker-container-1nbg</link>
      <guid>https://forem.com/danielcandidos/how-to-debug-a-python-and-django-application-inside-a-docker-container-1nbg</guid>
      <description>&lt;p&gt;The &lt;strong&gt;rollercoaster of emotions&lt;/strong&gt; experienced while developing systems is something that anyone with even minimal exposure to this can strongly state. However, for those with significant experience across various projects and applications, building a system from scratch is often considered the dream situation.&lt;/p&gt;

&lt;p&gt;The real nightmare arises when you need to maintain, support, or evolve an existing and running system - sometimes with thousands or millions of users. And the &lt;strong&gt;deepest layer&lt;/strong&gt; of The Inferno of Dante  in software development is when you have to &lt;strong&gt;debug the code to fix an issue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I know that sometimes, especially when you’re just starting out, you rely on the simplest and most informal &lt;code&gt;print()&lt;/code&gt; and its equivalents in other languages. &lt;strong&gt;No worries, our secret is safe here&lt;/strong&gt;. However, we also know that for the most painful and tricky bugs, we need to use an efficient, powerful, and comprehensive &lt;strong&gt;debugging tool&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I have been required to analyze some &lt;strong&gt;performance issues&lt;/strong&gt; in my work. A shallow investigation of function/class returns or time tracking with Grafana &lt;strong&gt;wasn’t enough&lt;/strong&gt; to find the real problem in all the tangled code. So... let's debug!&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging
&lt;/h2&gt;

&lt;p&gt;In Python, we have &lt;a href="https://docs.python.org/3/library/pdb.html" rel="noopener noreferrer"&gt;&lt;strong&gt;PDB&lt;/strong&gt;&lt;/a&gt;, a straightforward library for debugging our code. To use it, you simply need to install it using pip. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pdb 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add &lt;strong&gt;one line&lt;/strong&gt; in the code you want to debug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pdb; pdb.set_trace()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will be your &lt;strong&gt;breakpoint&lt;/strong&gt;. Now, you can run your application again, and it will pause execution right after the line you just added. You can then &lt;strong&gt;step through your code&lt;/strong&gt;, line by line and function by function, to find the issue you need to address.&lt;/p&gt;

&lt;h2&gt;
  
  
  With Docker
&lt;/h2&gt;

&lt;p&gt;The setup above &lt;strong&gt;should suffice for a simple Python project&lt;/strong&gt; with Django, Flask, or FastAPI. However, if you've reached this point and are running your application &lt;strong&gt;inside a Docker container&lt;/strong&gt;, facing issues, I've been on the same road. Stay calm and simply follow these next few steps:&lt;/p&gt;

&lt;p&gt;1 - Instead of the usual PDB, you should install the &lt;strong&gt;remote debugger&lt;/strong&gt;. Run it inside your Docker container (or include the library in your dependencies manager):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install remote-pdb 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2 - In your &lt;code&gt;docker-compose.yml&lt;/code&gt; file, &lt;strong&gt;add a new port&lt;/strong&gt; to expose the PDB entrypoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
services:
  your-app:
    ...
    ports:
      - 8000:8000 # already existing port of your application
      - 4444:4444 # NEW LINE TO EXPOSE PDB PORT
    ...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3 - Still in your &lt;code&gt;docker-compose.yml&lt;/code&gt; file, &lt;strong&gt;add these two lines&lt;/strong&gt;, to allow sending commands from outside Docker to your running container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
services:
  your-app:
    ...
    ports:
      - 8000:8000
      - 4444:4444
    stdin_open: true # THIS
    tty: true        # AND THIS
    ...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 - Finally, &lt;strong&gt;change the breakpoint line&lt;/strong&gt; in your code to this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;__import__("remote_pdb").set_trace(host='0.0.0.0', port=4444)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run your Docker image as well and connect with PDB debugger from &lt;strong&gt;port 4444&lt;/strong&gt;, which we opened, using a simple telnet command in your favorite terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;telnet 0.0.0.0 4444
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PDB Commands
&lt;/h2&gt;

&lt;p&gt;To cleverly sail through the sea of your code, you'll need to use the &lt;strong&gt;helpful commands&lt;/strong&gt; provided by PDB. Let's see some of them here:&lt;/p&gt;

&lt;p&gt;I normally just use the commands &lt;strong&gt;step (s)&lt;/strong&gt; and &lt;strong&gt;next (n)&lt;/strong&gt;. The difference between both is that &lt;code&gt;step&lt;/code&gt; execute each line, even those inside a function that is called on the current one. And &lt;code&gt;next&lt;/code&gt; only execute the lines inside the current function, waiting for the return of called functions.&lt;/p&gt;

&lt;p&gt;Other two useful commands are &lt;strong&gt;return (r)&lt;/strong&gt;, that execute all the function until it returns, and &lt;strong&gt;continue (c)&lt;/strong&gt;, that execute everything until the next breakpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this content has been helpful to you. Through investigating a significant performance issue and reinforcing my learning while writing this text, I can guarantee that&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"using the right tool for the right job”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;is something we should never forget as software developers. It can elevate our careers to a &lt;strong&gt;more effective level&lt;/strong&gt; and &lt;strong&gt;bring more joy&lt;/strong&gt; to this rollercoaster ride.&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>docker</category>
      <category>debug</category>
    </item>
  </channel>
</rss>
