Investigation times got you down? See why it’s time to switch to Honeycomb.Learn more

Reporting Exceptions to Honeycomb with Frontend Observability

There are at least two ways to report error details in OpenTelemetry. Web applications generally place exceptions in trace spans as span events, and mobile applications send exceptions as log messages instead. This article will help you understand which approaches you can use, and how the errors will appear in Honeycomb.

| December 15, 2025
Reporting Exceptions to Honeycomb with Frontend Observability

So you've built a client application and you've started sending telemetry. The information sent back by this client is vital to you, and one of the first things you care about is capturing and reporting errors.

There are at least two ways to report error details in OpenTelemetry. Web applications generally place exceptions in trace spans as span events, and mobile applications send exceptions as log messages instead. This article will help you understand which approaches you can use, and how the errors will appear in Honeycomb.

RUM can leave questions unanswered. Honeycomb for Frontend Observability doesn’t.

Honeycomb and exception visualizations

The platform reports errors by marking the span in error (red coloring and captured in the ‘Spans with Errors’ locator). It places a red circle around the exception event that marks the details:

placeOrder span with errors

In all cases, you should mark the span that received the error to a spanStatus of ERROR. The important piece is the code, which sets the error attribute to true.

// JavaScript Web example
span.setStatus(SpanStatusCode.ERROR);

// iOS example
span.status = .ok

// Android example:
span.setStatus(StatusCode.ERROR)

Providing a message in an error span

You may have noticed that you can add a message to your error spans when setting the span status. This does not add exception details. Instead, it adds a status_message to the span. According to the OpenTelemetry documentation, you should set the message to the exception message, if present:

// JavaScript Web example
span.setStatus({
  code: SpanStatusCode.ERROR,
  message: exception.getMessage()
);

// iOS example
span.status = .error(description: error.localizedDescription)

// Android example:
span.setStatus(StatusCode.ERROR, exception.getMessage())

The examples below all provide the status message.

Armed with the ability to set the span status and message, let's look at how to provide the exception details.

Attaching an exception to a trace span

Both the core OpenTelemetry Frontend SDKs and all Honeycomb Frontend SDKs support two mechanisms to attach an application exception to a span:

  • Using recordException on a span will add a span event to the span itself. Most OpenTelemetry APIs have a variant of this method and it is used in a lot of web frontend code.
  • Using a log record to record the exception and attach it to the span with the trace.trace_id and trace.parent_id attributes.

Let's look at each technique.

Recording an exception in a span event

Each trace span includes a collection of span events, which are visible in Honeycomb as colored circles marked at the time their event occurred.

colored circles marked at the time their event occurred

Span events for errors should be named exception. When attached to a span with an error status, they will be colored red, as shown in the third span above.

A web frontend records the exception into a span event using recordException. To do this, you must:

  1. Mark the span in error, and add the message to it (optionally)
  2. Use span.recordException to add the span event to the span containing the exception. It will automatically have the name exception
// get the current span (alternatively, create one and end it later)
const activeSpan = trace.getSpan(context.active());

try {
  // something
} catch (e) {
  activeSpan.setStatus({
     status: SpanStatusCode.ERROR,
     message: e.getMessage()
  ); 
  activeSpan.recordException(e);
}

This example assumes we have accessed or created an active span, and that we're just adding the exception to it, not ending it here.

This is not the only approach, but there is a lot of existing code and auto-instrumentation that uses this mechanism. The main issue with span events is that they are only sent once the span is ended and queued for transmission. This is why the second mechanism exists: recording the error as a log message.

Recording an exception with a trace-participating log message

In mobile telemetry, the preferred mechanism for capturing device crashes and exceptions is by attaching a log record to the trace and span. The log message can be sent right away, even if the span is never sent (due to forgetting to end the span or a crashing frontend).

Since mobile SDKs also all center their debugging around a logging mechanism, and logs are the currency of error messages in many systems, this approach is recommended.

With Android (using the Android OTel SDK):

  1. Mark the span in error and add the message to it (optionally)
  2. Grab the loggerProvider from the SDK instance
  3. Create a Logger instance
  4. Create a log message with key attributes including a stack trace, exception type, and the error.message, error.stacktrace, and error.type properties.

With iOS:

  1. Mark the span in error and add the message to it (optionally)
  2. Access the defaultErrorLogger from OpenTelemetryApi.Logger
  3. Use it to build a log record
  4. Emit the log record including a stack trace, exception type, and the same error properties as above

In general, you should probably create your own wrapper to define these values consistently, but Honeycomb's SDKs come to the rescue!

Using the Honeycomb logging helper methods

Honeycomb provides enablement SDKs for web and mobile-native applications to make onboarding easier. These SDKs send device-specific attributes in spans, enable device-specific telemetry, and other features. You’re using OpenTelemetry when you install these SDKs.

Honeycomb's mobile frontend SDKs provide helper methods to log your exceptions: Honeycomb.log in the Honeycomb Swift SDK, and Honeycomb.logException in the Honeycomb Android SDK.

iOS:

do {
  // something
} catch {
  span.status = .error(description: error.localizedDescription)

  Honeycomb.log(
    error: error,
    attributes: ["name" : .string("exception")],
    thread: Thread.main
  )
}

Android:

try { 
  // something
} catch (exception) {
  activeSpan.setStatus(StatusCode.ERROR, e.message) 

  rum?.let {  // assigns rum to 'it' below
    Honeycomb.logException(
      it
      exception,
      Attributes.of(
        AttributeKey.stringKey("name"),
        "exception",
      ),
      Thread.currentThread()
    )
  }
}

The examples above log the errors properly as log signals, and attach them to their traces in OpenTelemetry. Wrapper methods like these let Honeycomb add specific exception details as individual OpenTelemetry attributes, which helps in decoding stack traces, categorizing errors, and tracking duplicate error conditions during investigations.

How Honeycomb shows errors

Honeycomb stack traces will display spans with the error status with red span bars.

Honeycomb stack traces will display spans with the error status with red span bars

Not all errors generate exception events. Examples of errors without these events include errors that are retried, or expected, or happen frequently in an API that records errors but doesn't result in a failure in the code.

For errors that have recorded exception events, Honeycomb will show the event with a red circle, regardless of whether the exception comes from a span event or log message:

Honeycomb will show the event with a red circle

Click on the circle and the exception attributes will appear on the attribute explorer on the right hand side of the trace view:

Click on the circle and the exception attributes will appear on the attribute explorer on the right hand side of the trace view

You can review the type of signal used to record the exception in the ‘Fields’ panel. Both approaches show the meta.span_kind as span_event, so don't look there. Instead, look at the meta.signal_type field.

Exception span events have a signal type of trace:

signal type of trace

A trace-participating log exception is attached to a log signal. The signal type will be log:

signal type of log

This is how OpenTelemetry incorporates the log into the full trace: it adds the trace.trace_id attribute, which points to the trace itself, and the trace.parent_id attribute, which points to the trace span the log message is attached to:

This is how OpenTelemetry incorporates the log into the full trace

Which approach should I use?

Why use span events?

Span events are sent with the rest of the span. They don't require you to set up log ingest at the frontend tier, either. For web applications this can be an advantage. Span events are queryable separately, and cost the same as a separate log event.

Why trace-participating log messages?

As we discussed earlier, logs have several advantages over traces for frontend observability. They are sent immediately to the logging system, rather than waiting for a trace span to end, and can be sent even if your application crashes before a longer running span completes.

Armed with these concepts, you can confidently set your span statuses, provide span error status messages, and report errors either as span events or log messages.

Choose the method that best suits you: if you have configured your logging telemetry pipelines and are sending log messages, you can take advantage of trace-participating logs and get the messages immediately without fear of losing them due to crashes or improperly ended spans. If your existing web application heavily uses recordException, you can use span events.

If you have any further questions or need me to expand on some of these concepts, feel free to book time with me! I’d love to hear from you.