Cloudflare Tail Workers logging

Start logging in 5 minutes

Forward console.log output, uncaught exceptions and failed invocations from your existing Cloudflare Workers to Better Stack, without changing their code. A Tail Worker receives these events right after each invocation and sends them on.

Create a source

Create a new HTTP source in Better Stack.

Create the Tail Worker

Create a new Worker called better-stack-tail:

Create a new Worker
npm create cloudflare@latest -- better-stack-tail --type=hello-world --lang=js --no-deploy
cd better-stack-tail

Replace the contents of src/index.js with:

src/index.js
export default {
  async tail(events, env, ctx) {
    const logs = []

    for (const event of events) {
      const context = {
        worker: event.scriptName,
        outcome: event.outcome,
        request: event.event?.request && {
          method: event.event.request.method,
          url: event.event.request.url,
        },
      }

      for (const log of event.logs) {
        const message = log.message
          .map((part) => (typeof part === "string" ? part : JSON.stringify(part)))
          .join(" ")
        logs.push({ ...context, dt: log.timestamp, level: log.level, message })
      }

      for (const exception of event.exceptions) {
        logs.push({ ...context, dt: exception.timestamp, level: "error", message: `${exception.name}: ${exception.message}` })
      }

      // Record failed invocations without an exception, e.g. exceeded CPU or memory limits
      if (!["ok", "canceled"].includes(event.outcome) && event.exceptions.length === 0) {
        logs.push({ ...context, dt: event.eventTimestamp, level: "error", message: `Worker invocation ended with outcome ${event.outcome}` })
      }
    }

    if (logs.length === 0) return

    ctx.waitUntil(
      fetch("https://$INGESTING_HOST", {
        method: "POST",
        headers: {
          "Authorization": "Bearer $SOURCE_TOKEN",
          "Content-Type": "application/json",
        },
        body: JSON.stringify(logs),
      })
    )
  },
}


Deploy the Tail Worker:

Deploy the Tail Worker
npx wrangler deploy

Connect your Workers

Add better-stack-tail as a tail consumer in the Wrangler configuration of each Worker you want to collect logs from:

wrangler.jsonc wrangler.toml
{
  "tail_consumers": [{ "service": "better-stack-tail" }]
}
[[tail_consumers]]
service = "better-stack-tail"

Redeploy each of those Workers:

Redeploy your Worker
npx wrangler deploy

View your logs

Send a request to one of your Workers and open Better Stack β†’ Live tail

Each log carries the Worker's name in worker, the request's method and url in request, and the invocation's outcome.

Need help?

Please let us know at hello@betterstack.com.
We're happy to help! πŸ™