Responsive

Propagating the OTel Context from the Browser to the Backend

Propagating the OTel Context from the Browser to the Backend
Jul 2, 2024
5min
read
Oscar Reyes
Lead Software Engineer
Tracetest

Learn how to propagate user interactions from your browser to backend services seamlessly using OpenTelemetry's web auto-instrumentation library.

Share on X
Share on LinkedIn
Share on Reddit
Share on HackerNews
Copy URL

Table of Contents

Hola a todos! This is Oscar from Tracetest bringing you the first entry in the Tracetest Tips topic. Today we are starting with something spicy 🌶️ .

Starting the OpenTelemetry journey can be daunting, mostly because there are a lot of concepts to understand, libraries to install, and things to connect…

In this blog post, I will show you how to propagate user interactions from the browser side to your backend services in a simple way. So buckle up, put on your `use using useState` hats and let's begin.

Now, you have your precious front-end and back-end services running, you have followed every step of the OpenTelemetry docs, but for some reason you aren’t finding a way to make it work together!

We all have been there… believe me 🥴
## Understanding the Web Auto-instrumentation Library

The official web instrumentation package is [@opentelemetry/auto-instrumentations-web](https://www.npmjs.com/package/@opentelemetry/auto-instrumentations-web), which is an aggregation layer for the four main libraries.

**@opentelemetry/instrumentation-document-load -** Registers everything that happens after Javascript is ready to be executed, gathering information about the load speed of the different assets of the website.

**@opentelemetry/instrumentation-user-interaction -** For every user interaction, a new trace will be generated and the context propagated to the following steps, including outbound requests.

**@opentelemetry/instrumentation-fetch -** Every time the app sends a request using the global fetch object, a request span will be generated and the context will be propagated.

**@opentelemetry/instrumentation-xml-http-request -** Similar to the previous fetch library, but uses the HTTP request browser API instead.

## Fixing the Common Propagation Problems

### Scenario 1: No `traceparent` header in HTTP requests.

When sending a CORS request using `fetch` you’ll need to specify to which services you want the library to propagate to the traceparent header, in this case by changing the `registerInstrumentations` config like this:

```tsx
registerInstrumentations({
   tracerProvider: provider,
   instrumentations: getWebAutoInstrumentations({
     '@opentelemetry/instrumentation-fetch': {
         propagateTraceHeaderCorsUrls: /.*/, // here you can add a regex to match the services you want the header to be sent to
       },
   })
});
```

### Scenario 2: User interactions and Fetch requests appear in different traces, not linked together

This specific scenario owes me a couple of hours of my life.If you are using event handlers for click, submit or any other user interaction event, and those handlers use `async/await`, you could face this issue.

Having a handler similar to this:
```tsx
button.addEventListener('click', async (event) => {
await myAwesomeAsyncProcess();

const res = await fetch('https://my-awesome-service.com');
});
```

Would make the context from the initial user interaction event be dropped. To fix this (at least currently), switch the `await` for a Promise 🤷🏽.

```tsx
button.addEventListener('click', (event) => {
 myAwesomeAsyncProcess().then(async () => {
  const res = await fetch('https://my-awesome-service.com');
 });
});
```

### Scenario 3: I see the `traceparent` header and/or other propagation headers as part of the Network tab requests, but the context won’t propagate.

In this case, make sure that both the frontend and the backend instrumentation are speaking the same language. Part of the setup is to configure which technique or techniques will be used to propagate the trace information.

```tsx
provider.register({
   contextManager: new ZoneContextManager(),
   propagator: new CompositePropagator({
     propagators: [new W3CBaggagePropagator(), new W3CTraceContextPropagator()],
   }),
 });
```

In this example, we are registering a W3C Baggage and TraceContext Propagator, which produces the `traceparent` header, but other techniques like [**@opentelemetry/propagator-b3](https://www.npmjs.com/package/@opentelemetry/propagator-b3)**  can be used.

So, don’t be afraid to talk to your favorite Backend or Frontend person to be on the same page 😉.

## Takeaways

That’s it for the first Tracetest Tips entry 💪🏽.

I hope this was helpful and if you want to know more about how you can make the most of your instrument services check this docs page about [true end-to-end trace-based tests with Tracetest and Playwright](https://docs.tracetest.io/tools-and-integrations/playwright)! Also, feel free to join our [Slack community](https://dub.sh/tracetest-community), give [Tracetest a star on GitHub](https://github.com/kubeshop/tracetest), or [schedule a time to chat 1:1](https://calendly.com/ken-kubeshop/tracetest-walkthrough).