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.

OpenTelemetry in the Browser 

OpenTelemetry can be used in the browser to trace your frontend application. We recommend creating spans and events yourself. As of this writing, we do not feel that OpenTelemetry’s auto-instrumentation support for browsers is an effective choice for Honeycomb. You can utilize the full OpenTelemetry API to do manual instrumentation. The whitepaper has more detail on the tradeoffs of auto-instrumentation.

OpenTelemetry browser traces are sent via OTLP with HTTP/JSON. Honeycomb only supports directly ingesting data via OTLP with HTTP/protobuf or GRPC/protobuf. This means you will need to set up an OpenTelemetry Collector to accept any browser traces before you send them to Honeycomb. Routing through an OpenTelemetry Collector has the added benefit of being the recommended approach anyway since sensitive data is never safe in the browser, so adding your Honeycomb API keys to code exposed in the browser is not a great idea. By using a collector, you can store sensitive credentials there and ensure that any data being sent to Honeycomb is processed safely (along with any additional data control enabled by the collector).

Initialization 

The OpenTelemetry initialization needs to happen as early as possible in the webpage. Accomplish this by creating an initialization file, similar to the JavaScript example below. Then, load the initialization file at the top of your web page’s header or where you initialize your SPA.

import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { ZoneContextManager } from '@opentelemetry/context-zone';

const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const exporter = new OTLPTraceExporter({
  url: 'https://<your collector endpoint>:443/v1/traces'
});
const provider = new WebTracerProvider({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'browser',
  }),
});
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
provider.register({
  contextManager: new ZoneContextManager()
});

Note that the tracing.js example above calls the BatchSpanProcessor. Batching up span sending operations reduces client-side load and can help reduce battery and data usage from end user devices. Batching will also decrease contention with your application for network resources and ensure that your instrumentation creates minimal impact. The BatchSpanProcessor automatically batches events together.

You then need to add instrumentation for actions. Here, we show sample code that instruments the window load event and a button:

const { trace, context, } = require('@opentelemetry/api');
const tracer = trace.getTracer();
const root_span = tracer.startSpan('document_load');
//start span when navigating to page
root_span.setAttribute('pageUrlwindow', window.location.href);
window.onload = (event) => {
  // ... do loading things
  // ... attach timing information
 root_span.end(); //once page is loaded, end the span
};
button.clicked = (event) => {
  context.with(trace.setSpan(context.active(), root_span), () => {
    const span = tracer.startSpan('button_clicked', {attributes: {
    //Add your attributes to describe the button clicked here
    }});
  //do button things
  span.end();
  });
}

Instrumentation Packages 

To instrument your Web page, add the following packages:

npm install --save @opentelemetry/api
npm install --save @opentelemetry/sdk-trace-web
npm install --save @opentelemetry/exporter-trace-otlp-http
npm install --save @opentelemetry/context-zone

OpenTelemetry Collector Configuration 

The OpenTelemetry Collector will require an OTLP HTTP receiver enabled. You will also need to add the cors_allowed_origins property to enable Cross Origin Resource Sharing (CORS) from the browser to the collector. Your collector will need to be accessible by the browser. It is recommended to put an external load balancer in front of the collector.

In the example below, the configuration allows for the OpenTelemetry Collector to accept browser OpenTelemetry tracing, and is required to get data from the browser to Honeycomb.

If using the dataset-only data model, refer to the Honeycomb Classic tab for instructions. Not sure? Learn more about Honeycomb versus Honeycomb Classic.

receivers:
  otlp:
    protocols:
      http:
        endpoint: "0.0.0.0:4318"
        cors:
          allowed_origins: https://*.<yourdomain>.com
processors:
  batch:

exporters:
  otlp/honeycomb:
    endpoint: "api.honeycomb.io:443"
      headers:
        "x-honeycomb-team": "YOUR_API_KEY"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/honeycomb]
receivers:
  otlp:
    protocols:
      http:
        endpoint: "0.0.0.0:4318"
        cors:
          allowed_origins: https://*.<yourdomain>.com
processors:
  batch:

exporters:
  otlp/honeycomb:
    endpoint: "api.honeycomb.io:443"
      headers:
        "x-honeycomb-team": "YOUR_API_KEY"
        "x-honeycomb-dataset": "YOUR_DATASET"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/honeycomb]

Connecting the Frontend to the Backend 

To connect your frontend to the backend, you need to pass trace context in headers in the request.

If utilizing the instrumentation-xml-http-request package or the instrumentation-fetch package, set a configuration option to propagate the trace header automatically for you.

registerInstrumentations({
  instrumentations: [
    new XMLHttpRequestInstrumentation({
      propagateTraceHeaderCorsUrls: [
         /.+/g, //Regex to match your backend urls. This should be updated.
      ]
    }),
    new FetchInstrumentation({
      propagateTraceHeaderCorsUrls: [
         /.+/g, //Regex to match your backend urls. This should be updated.
      ]
    }),
  ],
});

Alternatively, if using the auto-instrumentation package for web, you can send this configuration property to the package:

registerInstrumentations({
 instrumentations: [
   getWebAutoInstrumentations({
     // load custom configuration for xml-http-request instrumentation
     '@opentelemetry/instrumentation-xml-http-request': {
       propagateTraceHeaderCorsUrls: [
           /.+/g, //Regex to match your backend urls. This should be updated.
         ],
     },
     '@opentelemetry/instrumentation-fetch': {
       propagateTraceHeaderCorsUrls: [
           /.+/g, //Regex to match your backend urls. This should be updated.
         ],
     },
   }),
 ],
});

Did you find what you were looking for?