Improving Sentry Logs

October 20, 2025 ¡ Tyler Yeager ¡ 2 min reading time

My first attempt to setup error logging was in a global state. Put it at the top, call it a day. But, this wasn’t really getting the logs that I wanted. I was missing errors I cared about and getting library errors that I didn’t care about.

Sending Messages

I have started using more manual capturing of error logs, rather than depending entirely on the automatic error capturing. I wanted two things

  • More breadcrumbs, as well as being more relevant
  • More error logging for specific issues I consider errors, such as logical errors.
Sentry.captureMessage(message)

This worked better, but it was still very limited in the amount of information it would carry. I thought this would work, but it didn’t provide any useful debug information.

Sentry.captureMessage(message, { level: 'warning', data: {
a: 1,
b: 2,
}})

I found this strange, because why didn’t it include the extra information? Searching around for why this wasn’t working, I stumbled on this Github issue

What I needed was scopes. With this, I could attach information about a specific event in its own scope(?)

So now I call sentry withScope, then set it as an extra debugData data field, and the error one (since it didn’t seem to show what I needed) For good measure, I still capture the message and place the exception in there too.

handleError.ts
Sentry.withScope((scope) => {
scope.setTag("file", "handleError.ts")
Sentry.captureMessage(message);
if(data) {
Sentry.setExtra("debugData", data)
}
if(error) {
Sentry.captureException(error);
Sentry.setExtra("errorInfo", {
type: error.type,
message: error.message,
stack: error.stack,
})
}
})

After discovering that Sentry also offers breadcrumbs, I started placing them in there too. Essentially, these allow you to see things that were just run before the error occurred. It doesn’t mean the error is related, but it allows you to see what was going on around it at that moment.

Sentry.addBreadcrumb({
a: 1,
b: 2,
});

Serverless

While building this for a serverless environment, I discovered that Sentry would capture the events, but not log them. There was no error messages or anything, it just wouldn’t send it.

The debug logs looked something like this

logs.txt
Sentry Logger [log]: Initializing Sentry: process: 314650, thread: main.
Sentry Logger [log]: Integration installed: InboundFilters
[...]
Sentry Logger [log]: Integration installed: LangChain
Sentry Logger [log]: SDK initialized from ESM
Sentry Logger [log]: @opentelemetry/api: Registered a global for diag v1.9.0.
Sentry Logger [log]: @opentelemetry/api: Registered a global for trace v1.9.0.
Sentry Logger [log]: @opentelemetry/api: Registered a global for propagation v1.9.0.
Sentry Logger [log]: @opentelemetry/api: Registered a global for context v1.9.0.
Sentry Logger [log]: Captured error event `Hello`
Sentry Logger [log]: Captured error event `abc is not defined`

Here’s the code too:

function.ts
sentry.captureMessage("Hello")
5 collapsed lines
try {
abc()
} catch(e) {
sentry.captureException(e)
}

You’ll notice that it a captures the error events. Unfortunately, it didn’t show up in my logs! After trying several iterations of this and confirming the config, I stumbled across this issue.

You need to flush the event in lambda handlers, otherwise, it won’t get delivered, as return ‘error’ you call at the end closes the handler and effectively freezes its execution.

So, to resolve I need to include a flush. I put one in with a 1000ms timeout and it started working.

sentry.captureMessage("Hello")
sentry.flush(1000)

Template

Here’s a template for functions to get things caught. It’s with bun, so you may need to change it around a little. Config for bun.

import * as Sentry from "@sentry/bun";
export default async function getSentry(name: string): Promise<typeof Sentry> {
const sentryConfig = getSentryConfig();
Sentry.init({
dsn: sentryConfig.config.dsn,
})
Sentry.setTag("function", name);
console.log("Returning inited Sentry")
return Sentry
}
export type SentryT = typeof Sentry;
import getSentry from "@code/getSentry";
import type { SentryT } from "@code/getSentry";
export async function main(data: dataT) {
const sentry = await getSentry("amazingFunction") as SentryT;
let response;
try {
response = await codeLogic(data);
} catch(e) {
sentry.captureException(e);
await sentry.flush(1000);
throw e;
}
return response;
}

Troubleshooting

This is the original version I had out. It caused a number of issues that I had to debug through. First of all, scope doesn’t have a method setExtra. You have to use Sentry. If you call Sentry.captureException and there is no exception, it will create an error log called <anonymous>.

Notice errorInfo only is set if error exists. However, I noticed while debugging that it is always sent, even if it doesn’t exist. I’m not sure why.

handleError.ts
Sentry.withScope((scope: ScopeT) => {
if(data) {
scope.setExtra("debugData", data)
}
if(error) {
Sentry.setExtra("errorInfo", {
type: error.type,
message: error.message,
stack: error.stack,
})
}
Sentry.captureMessage(message);
Sentry.captureException(error);
})

Did you find this interesting?

Consider subscribing 😊

No AI-generated content or SEO garbage.

Unsubscribe anytime