Send Logtape logs to Betterstack

Logtape is one of our favorite tools for logging. It's super easy, lightweight, and quick to set up. We also like to use Betterstack as it gives us an easy way to explore our logs and get notified when something happens.

But we run into a problem when we want to use Logtape with Betterstack. There are no existing sinks that automatically send logs to Betterstack. It's still possible though, and we'll show you how to do it.

Custom Logtape Sink to send logs to Betterstack

The way to send your logs to Betterstack is to directly call their Ingest API. Using the API, you can create a custom async sink that will send your logs to Betterstack.

const betterstackSink: AsyncSink = async (record) => {
  await fetch(process.env.BETTER_STACK_URL!, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.BETTER_STACK_TOKEN}`,
    },
    body: JSON.stringify({
      dt: record.timestamp,
      level: record.level,
      message: record.properties.message ?? record.message,
      properties: record.properties,
    }),
  });
};

await configure({
  sinks: {
    betterstack: fromAsyncSink(betterstackSink),
  },
  loggers: [{ category: [], sinks: ["betterstack"], lowestLevel: "info" }],
});

As you can see, in our use case we are sending record.properties.message as the message because we tend to use structured logging across our applications, like this:

logger.info({
  message: "User logged in",
  userId: 123,
});

But if you don't use structured logging, you can use record.message instead. The example provided should work for both cases.

2025-06-30

Thank you for reading! If you have any questions or feedback, please feel free to contact us at hi@davette.ca.

$