Skip to main content

OTLP Export

Embrace provides the @embrace-io/react-native-otlp package that can be used to export telemetry into a backend of choice.

The OpenTelemetry Protocol (OTLP) is an open standard that enables the transfer of observability data—such as traces and logs—from applications to various monitoring and analytics backends. By adopting OTLP, developers can send telemetry data in a consistent format, making integration with multiple backends straightforward.

This package provides an easy way to export trace and log data to any OTLP-compatible backend over HTTP. If the app_id / token values are provided the package also keeps sending telemetry data to Embrace, ensuring continuous observability with Embrace’s platform while allowing users to export data to other observability backends.

For more information about how Android / iOS Native custom export work please visit the OpenTelemetry Integration guide.

Install the package

npm:

npm install @embrace-io/react-native-otlp

yarn:

yarn add @embrace-io/react-native-otlp

For iOS you will also need to install or update pods for the application:

cd ios && pod install --repo-update

If you are not using Expo, you need to update your Metro configuration before running the application. To do this, open your metro.config.js file and set transformer.unstable_allowRequireContext to true:

// metro.config.js
module.exports = {
transformer: {
unstable_allowRequireContext: true,
// ... other configuration
},
};

This update is required because, under the hood, we check at runtime whether the @embrace-io/react-native-otlp package is installed. This package is mandatory if the OTLP feature is needed.

Expo applications use the @expo/metro-config package, which applies a default Metro configuration that enables this flag by default. As a result, this step is not necessary for Expo projects.

Implementation

For this example we will use Grafana Cloud in terms of redirecting telemetry data over there using OTLP endpoints. For more information about this please visit their online docs.

import React, {useEffect, useMemo, useState} from "react";
import {useEmbrace} from "@embrace-io/react-native";
import {View, Text} from "react-native";
import {Stack} from "expo-router";

const GRAFANA_TOKEN = "__GRAFANA_TOKEN__"; // `grafana_instance:token` converted into a base64 string.
const EXPORTER_CONFIG = {
logExporter: {
endpoint: "https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/logs",
headers: [
{
key: "Authorization",
token: `Basic ${GRAFANA_TOKEN}`,
},
],
},
traceExporter: {
endpoint:
"https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces",
headers: [
{
key: "Authorization",
token: `Basic ${GRAFANA_TOKEN}`,
},
],
},
};

// iOS is configurable through code, Android configuration happens at build time
const IOS_SDK_CONFIG = {appId: "__APP_ID__"};

function RootLayout() {
const {isPending, isStarted} = useEmbrace({
ios: IOS_SDK_CONFIG,
exporters: EXPORTER_CONFIG,
});

if (isPending) {
return (
<View>
<Text>Loading Embrace</Text>
</View>
);
} else {
if (!isStarted) {
console.log("An error occurred during Embrace initialization");
}
}

// regular content of the application
return (
<Stack>
<Stack.Screen name="(tabs)" options={{headerShown: false}} />
</Stack>
);
}

export default RootLayout;

Initializing in the Native layer

If you already have the Embrace React Native SDK initialized your native code or if you are planning to run the install scripts mentioned in our docs section you could still get the benefit of the OTLP custom export feature. Remember that the install scripts are adding the minimum code needed for initializing Embrace in the Native side but are not integrating the configuration for exporting the telemetry data into your backend of your choice. For this you would need to manually tweak both the Android/iOS sides.

iOS

If you already ran the install script mentioned above you would be able to find the EmbraceInitializer.swift file with some initial code that you can update:

import Foundation
import EmbraceIO
import RNEmbraceOTLP

let GRAFANA_AUTH_TOKEN = "Basic __YOUR TOKEN__"
let GRAFANA_TRACES_ENDPOINT = "https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces"
let GRAFANA_LOGS_ENDPOINT = "https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/logs"

@objcMembers class EmbraceInitializer: NSObject {
static func start() -> Void {
do {
// Preparing Span Exporter config with the minimum required
let traceExporter = OtlpHttpTraceExporter(endpoint: URL(string: GRAFANA_TRACES_ENDPOINT)!,
config: OtlpConfiguration(
headers: [("Authorization", GRAFANA_AUTH_TOKEN)]
)
)

// Preparing Log Exporter config with the minimum required
let logExporter = OtlpHttpLogExporter(endpoint: URL(string: GRAFANA_LOGS_ENDPOINT)!,
config: OtlpConfiguration(
headers: [("Authorization", GRAFANA_AUTH_TOKEN)]
)
)

try Embrace
.setup(
options: Embrace.Options(
appId: "__YOUR APP ID__",
platform: .reactNative,
export: OpenTelemetryExport(spanExporter: traceExporter, logExporter: logExporter) // passing the configuration into `export`
)
)
.start()
} catch let e {
print("Error starting Embrace \(e.localizedDescription)")
}
}
}

Android

Similar to iOS, if you already ran the install script you will see the following line already in place in your MainApplication file:

Embrace.getInstance().start(this)

Tweak the onCreate method using the following snippet to initialize the exporters with the minimum configuration needed.

Add the following imports:

import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter

And then the following in the OnCreate method:

// Preparing Span Exporter config with the minimum required
val spanExporter = OtlpHttpSpanExporter.builder()
.setEndpoint("https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces")
.addHeader("Authorization", "Basic __YOUR TOKEN__")

// Preparing Log Exporter config with the minimum required
val logExporter = OtlpHttpLogRecordExporter.builder()
.setEndpoint("https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/logs")
.addHeader("Authorization", "Basic __YOUR TOKEN__")

Embrace.getInstance().addSpanExporter(spanExporter.build())
Embrace.getInstance().addLogRecordExporter(logExporter.build())

// This is the line already added by the install script
Embrace.getInstance().start(this)

In case you want to avoid sending telemetry into Embrace you could do it by removing the app_id/token values from each Platform configuration. For more information please visit the how to Use without an Embrace account section.

Disable tracing for the OTLP export network requests

Embrace automatically creates spans for network requests, however because the OTLP export itself makes a network request this can produce a cycle where the export's network request creates a span which is then exported and then creates another span, etc.

To avoid this you can configure the endpoint to which you are exporting to be ignored on both Android and iOS:

Edit android/app/src/main/embrace-config.json in your app and add the disabled_url_patterns key. In the above example if you were exporting to Grafana your config would look like:

{
"app_id": "xxxxx",
"api_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"sdk_config": {
"app_framework": "react_native",
"networking": {
"enable_network_span_forwarding": true,
"disabled_url_patterns": [
"grafana.net"
]
}
}
}

See the Android Configure File page for more details.