Instrumenting Browser JavaScript Apps | Honeycomb

We use cookies or similar technologies to personalize your online experience & tailor marketing to you. Many of our product features require cookies to function properly.

Read our privacy policy I accept cookies from this site

Instrumenting Browser JavaScript Apps

If you send data about your web applications to Honeycomb, you probably already know to “start at the edge” and instrument each request as it reaches your servers. But for some types of applications — especially single-page apps (SPAs) and other JavaScript-intensive cases — you may find that instrumenting server requests isn’t enough to have good visibility into how users interact with your app. For these kinds of apps, you may want to send data directly from the browser to build up a richer view of how your app behaves in production.

Step 1: Add instrumentation JavaScript  🔗

To construct events to send to Honeycomb, build flat objects with string keys and values that are strings, numbers, and booleans.

// Example payload of an event to forward to Honeycomb
var someEvent = {
  type: "page-load",
  request_id: 123456,
  user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) SomeBrowser/123.45"
  asset_version: "1.232.90"
  timing_page_load_duration_ms: 2456.45,
  ab_group_touch_ui: true,
  ab_group_multi_team_chat: false,
  canary: false,
  window_height: 900,
  window_width: 1245,

// Honeycomb expects a JSON payload:
var payload = JSON.stringify(someEvent);

See our blog post about how we track page loads at Honeycomb for more ideas about what to track, including example code from the Honeycomb source.

If you don’t have pre-existing browser instrumentation, you can start with a RUM instrumentation library like Boomerang. You can configure Boomerang with plugins to collect data about page load timings and other characteristics of the in-browser experience, and URL-decode its payload format (a flat object) to forward directly to Honeycomb.

Step 2: Send events to Honeycomb  🔗

The safest way to send browser data to Honeycomb is to proxy it via a server-side web application that already has Honeycomb instrumentation. We recommend adding a dedicated endpoint for forwarding events to Honeycomb.

// HTTP handler for our `/honeycomb_proxy` forwarding endpoint
func (h *InstrumentationHandler) sendToHoneycomb(eventMetadata map[string]interface{}, user *types.User) {
  // Libhoney imported from
  ev := h.Libhoney.NewEvent()
  ev.Add(eventMetadata)      // The JSON event we constructed in the browser

  // Add any relevant fields that are easier to populate server-side
  ev.AddField("user_id", user.ID)
  ev.AddField("user_email", user.Email)

  // Send the event to the Honeycomb API
# Make a HoneycombController or add to an existing instrumentation controller
def send_to_honeycomb
  honeycomb_event = params[:honeycomb_event]
    # Whitelist keys you expect in your honeycomb events
      # ... plus probably many more keys!

  # Add any relevant fields that are easier to populate server-side
  honeycomb_event[:user_id] =
  honeycomb_event[:user_email] =

  # Send the event to the Honeycomb API
  # $honeycomb is a libhoney client instance

This endpoint can augment the event you construct in the browser with any fields that are easier to populate server-side (e.g. user email, team ID, plan type) before forwarding the event to Honeycomb. It also is a convenient place to add calculated fields using libraries that may be too large or complicated to include client-side. For example, you may want to use a geo-IP database to map the client IP address to approximate location, or map the client user agent to operating system, browser, and device fields that you attach to your Honeycomb events.

See our SDK docs for more information on how to send data to Honeycomb in the server-side language of your choice.

Can I send data directly to Honeycomb from the browser?  🔗

We currently caution against including your Honeycomb API key directly in your client-side JS. Your API key can be used to inject events into your datasets (and more, depending on the configured permissions of the API key), so the safest place to keep it is server-side with your backend application code.

However, for some cases—for example, if you maintain a client-only application that does not talk to any backend application servers—you may be willing to accept the risks of including your API key in your JavaScript to avoid maintaining a proxy. In these cases, we recommend creating a separate API key for exclusive use in your client-side JS and granting it only permission to send events.

// Example of sending an event directly from the browser to the Honeycomb API:
var honeycombWriteKey = "YOUR-HONEYCOMB-API-KEY-HERE";
var honeycombDatasetName = "Your App Name";
var someEvent = {
  // ... add fields to your honeycomb event here (see prior section)

var xhr = new XMLHttpRequest();"POST",  "" + encodeURIComponent(honeycombDatasetName), true);
xhr.setRequestHeader("X-Honeycomb-Team", honeycombWriteKey);

Can I use tracing?  🔗

Yes, but you’ll need to manually add Trace ID, Parent ID, Span ID, and duration fields to your Events to help Honeycomb correctly display your Events (Spans) in the waterfall view. See our Manual Tracing documentation for a list of fields you’ll need.

To see an example of manually capturing traces in browser javascript, check out the Browser Tracing Tutorial on the Microsoft Open Source blog.

Handling clock skew  🔗

There are two ways to set the timestamp on Honeycomb Events: either add the current time to a timestamp field on the Event before sending it to the Honeycomb API, or leave the field off and allow our API to automatically add the timestamp when the Event is received. For browser js and other code that runs client-side on your users' devices, we recommend that you allow our API to automatically set the timestamp — we’ve found that users' devices sometimes have local clocks that are hours, days, or years off from the actual time.

If you’d like to be able to track the transit time between your client-side code and the Honeycomb API, we recommend capturing the current time in your code as a UNIX timestamp in a custom field with a name like client_timestamp and adding a derived column that calculates the difference between the API-applied timestamp and the client timestamp: SUB(EVENT_TIMESTAMP(), $client_timestamp). Visualizing a HEATMAP of this derived column will give you a sense for how long Events take to move from the client to the Honeycomb API, and allow you to quickly spot outliers that come from wildly inaccurate client clocks.

Browser instrumentation best practices  🔗

Any information that’s available in the browser via JavaScript can be added to your events — the hardest part is knowing when to send them. Here are the points when we recommend sending an event to Honeycomb:

  • On page load: when the browser does a hard page load or navigation, this is a good time to send an event that includes Navigation Timing API data, Resource Timing data, and any attributes of the browser itself that you are curious about (screen size, user agent, capabilities).
  • Once per SPA navigation: if you have a single-page app (SPA), an app that performs navigations with pushState or replaceState, or an app that uses ajax to replace significant portions of the page, you’ll likely also want to send an event for each of these navigation-like scenarios. It should capture data about the timing of any relevant server requests as well as the time to render any UI components that are added or updated.
  • Once per significant user action: significant user interactions (conversions, ajax form submissions, etc.) may also be worth instrumenting if they aren’t represented by the events above. For example, an e-commerce site that supports one-click purchase via ajax might want to send an event each time a user clicks the purchase button, with information on the performance of the purchase http request and rendering of any post-purchase UI changes.

For more ideas on what data your instrumentation can send to Honeycomb, see our blog post on Instrumenting browser page loads at Honeycomb.