<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Context Managers on SpaceShaman</title>
    <link>https://spaceshaman.github.io/tags/context-managers/</link>
    <description>Recent content in Context Managers on SpaceShaman</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <copyright>SpaceShaman</copyright>
    <lastBuildDate>Thu, 27 Aug 2026 06:06:09 +0000</lastBuildDate>
    <atom:link href="https://spaceshaman.github.io/tags/context-managers/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to Add Logging Without Cluttering Your Code</title>
      <link>https://spaceshaman.github.io/posts/how-to-add-logging-without-cluttering-your-code/</link>
      <pubDate>Thu, 27 Aug 2026 06:06:09 +0000</pubDate>
      <guid>https://spaceshaman.github.io/posts/how-to-add-logging-without-cluttering-your-code/</guid>
      <description>How to use context managers and decorators to create detailed logs without cluttering business logic.</description>
      <content:encoded><![CDATA[<p>In one of my Python projects, I needed to create fairly detailed logs describing the program&rsquo;s execution. Most of the project is <em>glue code</em> that uses numerous external services and automates processes that were previously performed manually. I do not have full control over them, and my knowledge of how they work is very limited. Because different processes often use the same services, I wrapped those services in classes. The code implementing one of these automations looks roughly like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">clients.invoices</span> <span class="kn">import</span> <span class="n">InvoiceClient</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">clients.orders</span> <span class="kn">import</span> <span class="n">OrderClient</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">clients.partner</span> <span class="kn">import</span> <span class="n">PartnerClient</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">export_order</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">orders</span><span class="p">:</span> <span class="n">OrderClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">invoices</span><span class="p">:</span> <span class="n">InvoiceClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">partner</span><span class="p">:</span> <span class="n">PartnerClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">order</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">attachments</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_attachments</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">order</span><span class="o">.</span><span class="n">requires_invoice</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">invoice</span> <span class="o">=</span> <span class="n">invoices</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">order_id</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">positions</span><span class="o">=</span><span class="n">order</span><span class="o">.</span><span class="n">costs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">payment_term</span><span class="o">=</span><span class="mi">14</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">attachments</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">invoice</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">partner</span><span class="o">.</span><span class="n">send_order</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">attachments</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">orders</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">order_id</span><span class="p">,</span> <span class="s2">&#34;exported&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>At first, I added logging calls directly to the code, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">export_order</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">orders</span><span class="p">:</span> <span class="n">OrderClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">invoices</span><span class="p">:</span> <span class="n">InvoiceClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">partner</span><span class="p">:</span> <span class="n">PartnerClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Exporting order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">order</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Got order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">order</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Getting attachments for order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">attachments</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_attachments</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">order</span><span class="o">.</span><span class="n">requires_invoice</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Creating invoice for order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">invoice</span> <span class="o">=</span> <span class="n">invoices</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">order_id</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">positions</span><span class="o">=</span><span class="n">order</span><span class="o">.</span><span class="n">costs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">payment_term</span><span class="o">=</span><span class="mi">14</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Created invoice for order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">invoice</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">attachments</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">invoice</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Sending order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2"> to partner&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">partner</span><span class="o">.</span><span class="n">send_order</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">attachments</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Setting status of order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2"> to &#39;exported&#39;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">orders</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">order_id</span><span class="p">,</span> <span class="s2">&#34;exported&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>It is easy to see how cluttered the code has become. Add dozens of similar functions throughout the project, and the code becomes difficult to read and maintain.</p>
<p>My first step toward cleaning it up was to extract the repeated pattern into a separate context manager. After all, every stage of the process looked similar: record when it starts, perform the operation, and then record either its success or failure.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Iterator</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">contextmanager</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@contextmanager</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">log_step</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Iterator</span><span class="p">[</span><span class="kc">None</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Starting: </span><span class="si">{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Error during: </span><span class="si">{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Finished: </span><span class="si">{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>Everything before <code>yield</code> runs when execution enters the <code>with</code> block. If the block completes successfully, the <code>else</code> branch runs. If an exception occurs, the context manager records the failure and raises the exception again, so logging does not change the application&rsquo;s behavior.</p>
<p>I could now write the automation code like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">export_order</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">orders</span><span class="p">:</span> <span class="n">OrderClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">invoices</span><span class="p">:</span> <span class="n">InvoiceClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">partner</span><span class="p">:</span> <span class="n">PartnerClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">log_step</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Getting order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">order</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">attachments</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_attachments</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">order</span><span class="o">.</span><span class="n">requires_invoice</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="n">log_step</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Creating invoice for order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">invoice</span> <span class="o">=</span> <span class="n">invoices</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">order_id</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">positions</span><span class="o">=</span><span class="n">order</span><span class="o">.</span><span class="n">costs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">payment_term</span><span class="o">=</span><span class="mi">14</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">attachments</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">invoice</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">log_step</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Sending order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2"> to partner&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">partner</span><span class="o">.</span><span class="n">send_order</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">attachments</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">log_step</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Setting status of order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2"> to &#39;exported&#39;&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">orders</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">order_id</span><span class="p">,</span> <span class="s2">&#34;exported&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>This way, I still get an entry for the start, completion, or failure of every stage, but the business logic is no longer interleaved with pairs of nearly identical <code>logger.info</code> calls. The context manager also ensures that every event is recorded in a consistent format.</p>
<p>There is admittedly a little less code, but the result still does not look ideal. In more complex processes, successive <code>with</code> blocks introduce many additional levels of indentation, which can be cumbersome and significantly reduce readability.</p>
<p>At this point, I decided to use decorators for logging and apply them to methods of classes responsible for communicating with external services. The decorator did not need to implement the entire mechanism again—it only needed to execute the function inside the context manager I had already prepared:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">ParamSpec</span><span class="p">,</span> <span class="n">TypeVar</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">P</span> <span class="o">=</span> <span class="n">ParamSpec</span><span class="p">(</span><span class="s2">&#34;P&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&#34;R&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">log_function</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">R</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">with</span> <span class="n">log_step</span><span class="p">(</span><span class="n">message</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">wrapper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">decorator</span>
</span></span></code></pre></div><p>This decorator also works with instance methods. The wrapped function remains a descriptor, so Python automatically passes the instance as the first argument, <code>self</code>. Meanwhile, <code>@wraps</code> preserves the original method&rsquo;s name, documentation, and other metadata.</p>
<p>I could therefore move responsibility for logging into the client classes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">OrderClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Getting order&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Order</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Getting order attachments&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_attachments</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="n">Attachment</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">get_attachments</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Setting order status&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">set_status</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">order_id</span><span class="p">,</span> <span class="n">status</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">InvoiceClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Creating invoice&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">positions</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Cost</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="n">payment_term</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Invoice</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">create_invoice</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">order_id</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">positions</span><span class="o">=</span><span class="n">positions</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">payment_term</span><span class="o">=</span><span class="n">payment_term</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PartnerClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Sending order to partner&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">send_order</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">attachments</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Attachment</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">send_order</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">attachments</span><span class="p">)</span>
</span></span></code></pre></div><p>As a result, the process code returned almost to its original form:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">export_order</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">orders</span><span class="p">:</span> <span class="n">OrderClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">invoices</span><span class="p">:</span> <span class="n">InvoiceClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">partner</span><span class="p">:</span> <span class="n">PartnerClient</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">order</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">attachments</span> <span class="o">=</span> <span class="n">orders</span><span class="o">.</span><span class="n">get_attachments</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">order</span><span class="o">.</span><span class="n">requires_invoice</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">invoice</span> <span class="o">=</span> <span class="n">invoices</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">order_id</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">positions</span><span class="o">=</span><span class="n">order</span><span class="o">.</span><span class="n">costs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">payment_term</span><span class="o">=</span><span class="mi">14</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">attachments</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">invoice</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">partner</span><span class="o">.</span><span class="n">send_order</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">attachments</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">orders</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">order_id</span><span class="p">,</span> <span class="s2">&#34;exported&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>Logging now lives at the boundary where the application communicates with external systems, while the function describing the process once again focuses on its logic. Every call to a decorated method still generates an entry for its start, completion, or failure—regardless of how many automations use a given client.</p>
<p>This solution has an important limitation, however: the message passed to the decorator is static. Every order therefore produces exactly the same <code>Getting order</code> entry. While defining the class, I cannot use <code>f&quot;Getting order {order_id}&quot;</code> because the value of <code>order_id</code> does not exist until the method is called.</p>
<p>I wanted to retain the decorator&rsquo;s simple syntax while allowing the message to refer to function arguments:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">OrderClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Getting order </span><span class="si">{order_id}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Order</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span></code></pre></div><p>Simply calling <code>message.format(**kwargs)</code> is not enough. After all, the method can be called as either <code>get_order(order_id=&quot;123&quot;)</code> or <code>get_order(&quot;123&quot;)</code>. In the second case, the <code>kwargs</code> dictionary is empty, while <code>args</code> contains the client instance and the order identifier.</p>
<p>Python provides the appropriate tools for this in the <a href="https://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object"><code>inspect</code></a> module. The <code>signature()</code> function reads the decorated function&rsquo;s signature, while <a href="https://docs.python.org/3/library/inspect.html#inspect.Signature.bind"><code>Signature.bind()</code></a> maps the supplied positional and keyword arguments to its parameter names. The result is a <code>BoundArguments</code> object containing a ready-to-use mapping such as <code>{&quot;self&quot;: client, &quot;order_id&quot;: &quot;123&quot;}</code>.</p>
<p>The extended version of the decorator looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">inspect</span> <span class="kn">import</span> <span class="n">signature</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">ParamSpec</span><span class="p">,</span> <span class="n">TypeVar</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">P</span> <span class="o">=</span> <span class="n">ParamSpec</span><span class="p">(</span><span class="s2">&#34;P&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">&#34;R&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">log_function</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">message_template</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]],</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]]:</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="n">func_signature</span> <span class="o">=</span> <span class="n">signature</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">R</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">bound_args</span> <span class="o">=</span> <span class="n">func_signature</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">bound_args</span><span class="o">.</span><span class="n">apply_defaults</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="n">log_context</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">name</span><span class="p">:</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">                <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">bound_args</span><span class="o">.</span><span class="n">arguments</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">{</span><span class="s2">&#34;self&#34;</span><span class="p">,</span> <span class="s2">&#34;cls&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">message</span> <span class="o">=</span> <span class="n">message_template</span><span class="o">.</span><span class="n">format_map</span><span class="p">(</span><span class="n">log_context</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">with</span> <span class="n">log_step</span><span class="p">(</span><span class="n">message</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">wrapper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">decorator</span>
</span></span></code></pre></div><p>The signature is read only once, when the function is decorated. On every call, <code>bind()</code> builds a mapping of the supplied values, and <code>apply_defaults()</code> fills in the default values of omitted arguments. I remove <code>self</code> and <code>cls</code> from that mapping to prevent the representation of an entire instance from accidentally ending up in the message. Finally, <code>format_map()</code> substitutes the selected values into the template.</p>
<p>The decorators can now use method arguments regardless of how those arguments are passed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">OrderClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Getting order </span><span class="si">{order_id}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Order</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">get_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Getting attachments for order </span><span class="si">{order_id}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_attachments</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="n">Attachment</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">get_attachments</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Setting order </span><span class="si">{order_id}</span><span class="s2"> status to </span><span class="si">{status}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">set_status</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">order_id</span><span class="p">,</span> <span class="n">status</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">InvoiceClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Creating invoice for order </span><span class="si">{order_id}</span><span class="s2"> &#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;with a </span><span class="si">{payment_term}</span><span class="s2">-day payment term&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">positions</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Cost</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="n">payment_term</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Invoice</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">create_invoice</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">order_id</span><span class="o">=</span><span class="n">order_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">positions</span><span class="o">=</span><span class="n">positions</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">payment_term</span><span class="o">=</span><span class="n">payment_term</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PartnerClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@log_function</span><span class="p">(</span><span class="s2">&#34;Sending order </span><span class="si">{order.id}</span><span class="s2"> to partner&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">send_order</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">order</span><span class="p">:</span> <span class="n">Order</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">attachments</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Attachment</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_api</span><span class="o">.</span><span class="n">send_order</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">attachments</span><span class="p">)</span>
</span></span></code></pre></div><p><a href="https://docs.python.org/3/library/string.html#format-string-syntax">Format string syntax</a> supports references not only to arguments themselves but also to their attributes. Therefore, <code>{order.id}</code> retrieves only the identifier from an <code>Order</code> object. The resulting logs become much more useful:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Starting: Getting order 123
</span></span><span class="line"><span class="cl">Finished: Getting order 123
</span></span><span class="line"><span class="cl">Starting: Creating invoice for order 123 with a 14-day payment term
</span></span><span class="line"><span class="cl">Finished: Creating invoice for order 123 with a 14-day payment term
</span></span></code></pre></div><p>I chose not to append every argument automatically. A list of costs, attachments, or the entire order object could generate enormous entries and, worse, expose personal data, tokens, or passwords. The template forces a deliberate choice of the information needed to diagnose a problem—usually an order identifier, the operation name, and the new status. It is also important to remember that a typo in a template field name raises a <code>KeyError</code>, so decorators like this should be covered by tests.</p>
<p>Ultimately, a small abstraction allowed me to retain detailed, consistent logs without letting them obscure the program&rsquo;s actual logic. It does not solve every logging problem, but it has worked very well in code that integrates numerous external services.</p>
<p>What does logging look like in your projects? Do you prefer explicit logger calls, decorators, or another solution? Where do you draw the line between useful context and too much information in your logs?</p>
<p><em>You can also read this article on <a href="https://coderlegion.com/25521/how-to-add-logging-without-cluttering-your-code">CoderLegion</a>.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
