OTLP export
OTLP export
You can export traces and logs from the Embrace SDK to any OTLP-compatible backend, either alongside Embrace or instead of it.
The OpenTelemetry Protocol (OTLP) is an open standard for sending observability data, such as traces and logs, to monitoring and analytics backends. Because OTLP uses a consistent format, you can send the same telemetry to multiple backends.
To export telemetry, add OTLP exporters to the native Android and iOS SDKs when you start Embrace. For more on how custom export works in the native SDKs, see the OpenTelemetry integration guide.
You must add exporters before the Embrace SDK starts. If you start Embrace from JavaScript, first move SDK startup to native code as described in Start Embrace SDK in the native side.
Initializing in the native layer
The following examples export traces and logs to Grafana Cloud over HTTP. Replace the endpoints and Authorization header with the values for your backend. For details on Grafana's OTLP endpoints, see their docs.
- iOS
- Android
-
Add the OpenTelemetry Swift OTLP HTTP exporter to your app target. If you use CocoaPods, add the following to your app target in your
Podfile, then runpod install:pod 'OpenTelemetry-Swift-Protocol-Exporter-Http', '~> 2.1'If you source the Embrace Apple SDK from Swift Package Manager, add the
OpenTelemetryProtocolExporterHTTPproduct fromhttps://github.com/open-telemetry/opentelemetry-swiftto your app target in Xcode instead.The Embrace Apple SDK works with version 2.1 or newer of OpenTelemetry Swift.
-
In your
EmbraceInitializer.swiftfile, pass the exporters through theoteloption:import Foundationimport EmbraceIOimport OpenTelemetryProtocolExporterHttp@objcMembers class EmbraceInitializer: NSObject {static func start() -> Void {let grafanaToken = "__GRAFANA_TOKEN__" // `grafana_instance:token` converted into a base64 stringlet urlConfig = URLSessionConfiguration.defaulturlConfig.httpAdditionalHeaders = ["Authorization": "Basic \(grafanaToken)"]let httpClient = BaseHTTPClient(session: URLSession(configuration: urlConfig))let spanExporter = OtlpHttpTraceExporter(endpoint: URL(string: "https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces")!,httpClient: httpClient)let logExporter = OtlpHttpLogExporter(endpoint: URL(string: "https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/logs")!,httpClient: httpClient)do {try EmbraceIO.setup(options: .withAppId("__APP_ID__", // 5 digits stringplatform: .reactNative,otel: EmbraceIO.OTelOptions(spanExporter: spanExporter,logExporter: logExporter)))try EmbraceIO.shared.start()} catch let e {print("Error starting Embrace \(e.localizedDescription)")}}}
-
Add the Embrace OpenTelemetry Java module and the OTLP exporter to your app's
build.gradlefile (usually located at<project root>/android/app/build.gradle):dependencies {implementation "io.embrace:embrace-android-otel-java:${findProject(':embrace-io_react-native').properties['emb_android_sdk']}"implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.62.0") {// The OTLP exporter pulls in OkHttp 5. Exclude it to use the OkHttp 4 version that React Native provides.exclude group: 'com.squareup.okhttp3', module: 'okhttp'}}Match the exporter version to the OpenTelemetry Java SDK version your Embrace Android SDK uses. The Android changelog lists each OpenTelemetry update, and the React Native changelog lists the Android SDK version for each release.
-
In your
MainApplication.ktor.javafile, add the exporters before you start Embrace:
- Kotlin
- Java
import io.embrace.android.embracesdk.Embrace
import io.embrace.android.embracesdk.otel.java.addJavaLogRecordExporter
import io.embrace.android.embracesdk.otel.java.addJavaSpanExporter
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter
...
class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()
val grafanaToken = "__GRAFANA_TOKEN__" // `grafana_instance:token` converted into a base64 string
// Exporters must be added before Embrace starts
Embrace.addJavaSpanExporter(
OtlpHttpSpanExporter.builder()
.setEndpoint("https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces")
.addHeader("Authorization", "Basic $grafanaToken")
.build()
)
Embrace.addJavaLogRecordExporter(
OtlpHttpLogRecordExporter.builder()
.setEndpoint("https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/logs")
.addHeader("Authorization", "Basic $grafanaToken")
.build()
)
Embrace.start(this)
}
}
import io.embrace.android.embracesdk.Embrace;
import io.embrace.android.embracesdk.otel.java.EmbraceOtelJavaExtensionsKt;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
...
public class MainApplication extends Application implements ReactApplication {
@Override
public void onCreate() {
super.onCreate();
String grafanaToken = "__GRAFANA_TOKEN__"; // `grafana_instance:token` converted into a base64 string
// Exporters must be added before Embrace starts
EmbraceOtelJavaExtensionsKt.addJavaSpanExporter(
Embrace.INSTANCE,
OtlpHttpSpanExporter.builder()
.setEndpoint("https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces")
.addHeader("Authorization", "Basic " + grafanaToken)
.build()
);
EmbraceOtelJavaExtensionsKt.addJavaLogRecordExporter(
Embrace.INSTANCE,
OtlpHttpLogRecordExporter.builder()
.setEndpoint("https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/logs")
.addHeader("Authorization", "Basic " + grafanaToken)
.build()
);
Embrace.INSTANCE.start(this);
}
}
If you provide an app ID, Embrace keeps receiving your telemetry alongside your backend. To send telemetry only to your backend, see Use without an Embrace account.
For custom exporters, processors, and other options, see the native SDK docs:
Disable tracing for the OTLP export network requests
Embrace automatically creates spans for network requests. Because OTLP export also makes network requests, each export can create a span that is then exported, which creates another span, and so on.
To avoid this loop, configure Embrace to ignore your export endpoint on both Android and iOS:
- iOS
- Android
In your EmbraceInitializer.swift file, add the endpoint to ignoredURLs and pass the capture services through the captureServices option. For the Grafana example above:
let captureServices = CaptureServicesOptionsBuilder()
.addUrlSessionCaptureService(withOptions: URLSessionCaptureService.Options(
ignoredURLs: ["grafana.net"]
))
.addDefaults()
.build()
try EmbraceIO.setup(
options: .withAppId(
"__APP_ID__", // 5 digits string
platform: .reactNative,
captureServices: captureServices,
otel: EmbraceIO.OTelOptions(
spanExporter: spanExporter,
logExporter: logExporter
)
)
)
try EmbraceIO.shared.start()
If you still start Embrace from JavaScript, add disabledUrlPatterns: ["grafana.net"] to sdkConfig.ios instead.
Edit android/app/src/main/embrace-config.json in your app and add the disabled_url_patterns key. For the Grafana example above:
{
"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.
Configure export in JavaScript (deprecated)
@embrace-io/react-native-otlp is deprecated and will be removed in the next major release. It only works when you start the Embrace SDK from JavaScript, which is also deprecated. Use native export instead.
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 && USE_FRAMEWORKS=dynamic 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;
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 see our guide on how to use Embrace without an account.