The core API of Ottrelite provides a set of base classes and common APIs that allow to integrate or extend the Ottrelite tracing framework.
The aim of this project is to provide a unified API for performance tracing in React Native applications, allowing developers to easily instrument their apps with various backends for tracing, profiling, and performance monitoring.
The core package - @ottrelite/core
- provides a so-called Development API (which is designed to be consumed by programmers) for instrumenting your application for purely development use cases (profiling your code, tracing performance issues, etc.). The actual implementation of recording / displaying / reporting of the data is provided by individual, pluggable packages (called backends). An arbitrary number of backends (0, 1 or more) can be installed simultaneously, allowing you to use multiple backends at once, or switch between them as needed.
For production use cases, Ottrelite integrates with OpenTelemetry (OTEL) JS API, supporting all OTEL community instrumentations, custom processors, etc. The only limitation is that the setup must follow our guidelines, which are described in this README.
Feature | Status |
---|---|
C++ API | ✅ |
JS API | ✅ |
Swift API | ⏳ (in progress) |
Java/Kotlin API | ✅ |
RN integration | ✅ |
Currently, Android builds are not working yet. This is because we didn't integrate the native C++ code builds into Gradle yet. Please stay tuned for new updates soon. If you want to try out the Android API with RN internals tracing, please switch to the feature/gradle-plugin
branch.
Feature | Status |
---|---|
OTEL-JS interop | ✅ |
OTEL-CPP interop | ✅ |
OTEL-Java interop | TBD |
OTEL-Swift interop | TBD |
RN integration | ✅ |
Platform | Status |
---|---|
iOS | ⚠️ - only Systrace.js enabled |
Android | ✅ |
First, install the package with:
Then, follow the instructions from the respective section(s) below, depending on whether your use case involves production-time telemetry, development-time tracing or both.
Backend Name | Description | Platform(s) | OTEL compatiblity | Documentation page |
---|---|---|---|---|
@ottrelite/backend-platform | Platform-default tracing backend (ATrace for Android, OSSignposter for iOS) | Android, iOS | ✅ Yes | here |
@ottrelite/backend-wrapper-tracy | Tracy profiler backend for Android and iOS | Android, iOS | ❌ Incompatible (lack of support for async API) | here |
The quickest way to use Ottrelite is via the Development API. This API resembles the RN Systrace API and indeed 'revives' the no-op Systrace
export of RN. Calls to this API are reported to Ottrelite backends and can be previewed .
To use it, install Ottrelite Core and register backends you'd like to use, include the following Ottrelite.install()
call in your entrypoint file (e.g. index.js
):
In case of debugging performance problems, tracing usually makes sense only in release builds (used in a development environment, i.e., not in production). Debug builds are unoptimized (both the native and non-native sides) and therefore their performance may not be representative and could greatly differ from the performance of release builds.
At the same time, depending on your use case, you may want to include a package in production-deployed release builds. Please remember not to install()
the development-only backends in production-deployed builds and exclude them from even being packaged with the binary using react-native.config.js
file, as per the RN CLI documentation (iOS, Android)
To record synchronous traces, you can use the Ottrelite.beginEvent()
and Ottrelite.endEvent()
methods. The beginEvent
method starts a new event (imagine it is put on top of a stack), while the endEvent
method ends the most recent event (analogy: popping it off the stack). The event name is a string that identifies the event.
Events maintain a hierarchical structure, so you can nest them. If you begin a new event while another one is already in progress, the new event will be a child of the previous one.
To record asynchronous traces, you can use the Ottrelite.beginAsyncEvent()
and Ottrelite.endAsyncEvent()
methods. The beginAsyncEvent
method starts a new asynchronous event, while the endAsyncEvent
method ends the most recent asynchronous event.
To record counter events, you can use the Ottrelite.counterEvent()
method. This method allows you to record a numeric value over time, which can be useful for tracking metrics such as performance counters.
React Native exports its own Systrace
API, which supports synchronous, asynchronous and counter events. While this API is mostly used for development of RN itself, it is still exposed for production apps, but does nothing (i.e., calls are no-ops).
Ottrelite optionally (when reviveSystraceAPI
is set to true
) defines global variables that this Systrace
API uses, effectively making these calls effective. For instance, if you launch the app, you will see a hierarchy of events related to require()
invocations.
It is advised to use the Ottrelite
API instead of the Systrace
API, as the former is more flexible and provides a unified interface for all backends. The Systrace
API is provided for compatibility with existing code that uses it.
The synchronous and counter APIs of Systrace
and Ottrelite
are interoperable, i.e., an event began with one API can be ended with the other. However, the asynchronous APIs are not interoperable (the tokens / cookies returned by Systrace
are numbered differently than these of Ottrelite
and could cause collisions if passed crossing API boundaries), so you must use either Ottrelite
or Systrace
for given events, but not mix them at start & end of a given event.
Each of the backends provides a different way of recording traces, so the exact usage will depend on the backend you choose to use. Please consult their individual README.md
files for instructions.
The examples/rn-app
directory contains an example app presenting most of the features. After running pnpm i
in the root directory, you can cd
to it and run pnpm android
or pnpm ios
.
The examples/ottrelite-consumer-lib
directory contains an example native module (in this case, a Nitro Module, but this is not required) that consumes the Ottrelite C++ API, the Ottrelite Kotlin/Java API and the Ottrelite Swift/Objective-C API.
The examples/backend-jaeger
directory contains a Docker Compose file for running the Jaeger backend service. When run (docker compose up -d
), it will start the Jaeger UI to collect OTEL data from the example app. Then, just run the app on the device you've run Jaeger on (otherwise, you will need to adjust endpoint path to match your server's address) and you should start seeing traces in the Jaeger UI.
The roadmap for the nearest future includes the following: