---
title: Debugging logs · Cloudflare Workers docs
description: Send debugging information in an errored response to a logging service.
lastUpdated: 2025-04-28T14:11:18.000Z
source_url:
  html: https://77261bbd.preview.developers.cloudflare.com/workers/examples/debugging-logs/index.md
  md: https://77261bbd.preview.developers.cloudflare.com/workers/examples/debugging-logs/
---

If you want to get started quickly, click on the button below.

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/docs-examples/tree/main/workers/debugging-logs)

This creates a repository in your GitHub account and deploys the application to Cloudflare Workers.

* JavaScript

  ```js
  export default {
    async fetch(request, env, ctx) {
      // Service configured to receive logs
      const LOG_URL = "https://log-service.example.com/";


      async function postLog(data) {
        return await fetch(LOG_URL, {
          method: "POST",
          body: data,
        });
      }


      let response;


      try {
        response = await fetch(request);
        if (!response.ok && !response.redirected) {
          const body = await response.text();
          throw new Error(
            "Bad response at origin. Status: " +
              response.status +
              " Body: " +
              // Ensure the string is small enough to be a header
              body.trim().substring(0, 10),
          );
        }
      } catch (err) {
        // Without ctx.waitUntil(), your fetch() to Cloudflare's
        // logging service may or may not complete
        ctx.waitUntil(postLog(err.toString()));
        const stack = JSON.stringify(err.stack) || err;
        // Copy the response and initialize body to the stack trace
        response = new Response(stack, response);
        // Add the error stack into a header to find out what happened
        response.headers.set("X-Debug-stack", stack);
        response.headers.set("X-Debug-err", err);
      }
      return response;
    },
  };
  ```

* TypeScript

  ```ts
  interface Env {}
  export default {
    async fetch(request, env, ctx): Promise<Response> {
      // Service configured to receive logs
      const LOG_URL = "https://log-service.example.com/";


      async function postLog(data) {
        return await fetch(LOG_URL, {
          method: "POST",
          body: data,
        });
      }


      let response;


      try {
        response = await fetch(request);
        if (!response.ok && !response.redirected) {
          const body = await response.text();
          throw new Error(
            "Bad response at origin. Status: " +
              response.status +
              " Body: " +
              // Ensure the string is small enough to be a header
              body.trim().substring(0, 10),
          );
        }
      } catch (err) {
        // Without ctx.waitUntil(), your fetch() to Cloudflare's
        // logging service may or may not complete
        ctx.waitUntil(postLog(err.toString()));
        const stack = JSON.stringify(err.stack) || err;
        // Copy the response and initialize body to the stack trace
        response = new Response(stack, response);
        // Add the error stack into a header to find out what happened
        response.headers.set("X-Debug-stack", stack);
        response.headers.set("X-Debug-err", err);
      }
      return response;
    },
  } satisfies ExportedHandler<Env>;
  ```

* Python

  ```py
  import json
  import traceback
  from pyodide.ffi import create_once_callable
  from js import Response, fetch, Headers


  async def on_fetch(request, _env, ctx):
      # Service configured to receive logs
      log_url = "https://log-service.example.com/"


      async def post_log(data):
          return await fetch(log_url, method="POST", body=data)


      response = await fetch(request)


      try:
          if not response.ok and not response.redirected:
              body = await response.text()
          # Simulating an error. Ensure the string is small enough to be a header
          raise Exception(f'Bad response at origin. Status:{response.status} Body:{body.strip()[:10]}')
      except Exception as e:
          # Without ctx.waitUntil(), your fetch() to Cloudflare's
          # logging service may or may not complete
          ctx.waitUntil(create_once_callable(post_log(e)))
          stack = json.dumps(traceback.format_exc()) or e
          # Copy the response and add to header
          response = Response.new(stack, response)
          response.headers["X-Debug-stack"] = stack
          response.headers["X-Debug-err"] = e


      return response
  ```

* Hono

  ```ts
  import { Hono } from 'hono';


  // Define the environment with appropriate types
  interface Env {}


  const app = new Hono<{ Bindings: Env }>();


  // Service configured to receive logs
  const LOG_URL = "https://log-service.example.com/";


  // Function to post logs to an external service
  async function postLog(data: string) {
    return await fetch(LOG_URL, {
      method: "POST",
      body: data,
    });
  }


  // Middleware to handle error logging
  app.use('*', async (c, next) => {
    try {
      // Process the request with the next handler
      await next();


      // After processing, check if the response indicates an error
      if (c.res && (!c.res.ok && !c.res.redirected)) {
        const body = await c.res.clone().text();
        throw new Error(
          "Bad response at origin. Status: " +
          c.res.status +
          " Body: " +
          // Ensure the string is small enough to be a header
          body.trim().substring(0, 10)
        );
      }


    } catch (err) {
      // Without waitUntil, the fetch to the logging service may not complete
      c.executionCtx.waitUntil(
        postLog(err.toString())
      );


      // Get the error stack or error itself
      const stack = JSON.stringify(err.stack) || err.toString();


      // Create a new response with the error information
      const response = c.res ?
        new Response(stack, {
          status: c.res.status,
          headers: c.res.headers
        }) :
        new Response(stack, { status: 500 });


      // Add debug headers
      response.headers.set("X-Debug-stack", stack);
      response.headers.set("X-Debug-err", err.toString());


      // Set the modified response
      c.res = response;
    }
  });


  // Default route handler that passes requests through
  app.all('*', async (c) => {
    return fetch(c.req.raw);
  });


  export default app;
  ```
