Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/khaki-cups-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ottrelite/interop-otel': patch
---

feat!: deintegrate C++ OTLP exporter wrapper feature from package
5 changes: 5 additions & 0 deletions .changeset/late-crabs-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ottrelite/core': patch
---

feat: enabled install option and setEnabled runtime method for Ottrelite that control runtime tracing enabled state
6 changes: 6 additions & 0 deletions .changeset/slow-doors-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@ottrelite/backend-wrapper-tracy': patch
'@ottrelite/backend-platform': patch
---

feat: defer createHybridObject to first access moment
17 changes: 15 additions & 2 deletions examples/backend-jaeger/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
version: '3'

services:
proxy:
# fix for OpenTelemetry JS always adding a trailing slash to the OTLP endpoints, which is not supported by Jaeger
image: nginx:alpine
ports:
- '4318:4318'
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
jaeger:
image: jaegertracing/jaeger:2.9.0
hostname: jaeger
ports:
- '16686:16686' # Jaeger UI
- '4317:4317' # OTLP gRPC receiver
- '4318:4318' # OTLP HTTP receiver
- '4319:4319' # OTLP HTTP receiver for proxy (4318 served by nginx)
command:
[
'--set=receivers.otlp.protocols.http.endpoint=0.0.0.0:4319',
'--set=receivers.otlp.protocols.grpc.endpoint=0.0.0.0:4317',
]
environment:
- LOG_LEVEL=debug
- LOG_LEVEL=verbose
16 changes: 16 additions & 0 deletions examples/backend-jaeger/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
server {
listen 4318;
server_name _;


location / {
# remove trailing slashes
rewrite ^/(.*)/+$ /$1 break;

proxy_pass http://jaeger:4319;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
34 changes: 19 additions & 15 deletions examples/ottrelite-consumer-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,31 @@ import type { NitroOttreliteConsumerLibPlatform } from './specs/NitroOttreliteCo

export type { NitroOttreliteConsumerLib } from './specs/NitroOttreliteConsumerLib';

const NitroOttreliteConsumerLibHybridObject =
NitroModules.createHybridObject<NitroOttreliteConsumerLibCpp>(
'NitroOttreliteConsumerLibCpp'
);

const NitroOttreliteConsumerLibPlatformHybridObject =
NitroModules.createHybridObject<NitroOttreliteConsumerLibPlatform>(
'NitroOttreliteConsumerLibPlatform'
);

export class CppConsumerLib {
static hybridObject: NitroOttreliteConsumerLibCpp;
static generateImage(width: number, height: number): number[] {
return NitroOttreliteConsumerLibHybridObject.generateImage(width, height);
if (!this.hybridObject) {
this.hybridObject =
NitroModules.createHybridObject<NitroOttreliteConsumerLibCpp>(
'NitroOttreliteConsumerLibCpp'
);
}

return this.hybridObject.generateImage(width, height);
}
}

export class PlatformConsumerLib {
static hybridObject: NitroOttreliteConsumerLibPlatform;

static generateImage(width: number, height: number): number[] {
return NitroOttreliteConsumerLibPlatformHybridObject.generateImage(
width,
height
);
if (!this.hybridObject) {
this.hybridObject =
NitroModules.createHybridObject<NitroOttreliteConsumerLibPlatform>(
'NitroOttreliteConsumerLibPlatform'
);
}

return this.hybridObject.generateImage(width, height);
}
}
71 changes: 69 additions & 2 deletions examples/rn-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@ This is a demonstrator application for the Ottrelite project, showcasing how to
- [Getting Started](#getting-started)
- [Step 1: Start Metro](#step-1-start-metro)
- [Step 2: Build and run the app](#step-2-build-and-run-the-app)
- [Build types](#build-types)
- [Debug build](#debug-build)
- [Release build](#release-build)
- [Profiling build](#profiling-build)
- [Android](#android)
- [Debug build](#debug-build-1)
- [Release build](#release-build-1)
- [Profiling build](#profiling-build-1)
- [iOS](#ios)
- [Debug build](#debug-build-2)
- [Release build](#release-build-2)
- [Profiling build](#profiling-build-2)
- [Step 3: Trace the application](#step-3-trace-the-application)

---
Expand All @@ -30,12 +40,57 @@ pnpm start

With Metro running, open a new terminal window/pane from the root of your React Native project, and use one of the following commands to build and run your Android or iOS app:

### Build types


#### Debug build

The `debug` build variant (Android) or `Debug` configuration (iOS) will build the app in debug mode, with tracing enabled by default.

```sh
pnpm android
```

#### Release build

The `release` build variant (Android) or `Release` configuration (iOS) will build the app in release mode with the env var `DISABLE_TRACING` set to `true`, causing Ottrelite tracing to be disabled at runtime.

**Please note** that disabling of tracing at runtime happens only via `pnpm {android,ios}:release` script, not if building from Android Studio or Xcode, in which case tracing will be disabled. This is just the exemplary setup for this demo, in a usual case you'd likely want the inverted logic, in which case tracing is disabled by default and you only enable it when the env var is set instead. Handling of this env var is done by Babel plugin `transform-inline-environment-variables` and the `pnpm {android,ios}:release` script which passes the proper value of `DISABLE_TRACING` env var.

```sh
pnpm android:release
```

#### Profiling build

The `profiling` build variant (Android) or `Profiling` configuration (iOS) inherits everything from the `release` build variant and will build the app in profiling mode. This is the perfect mode that gives reliable performance behaviour after all compile-time optimizations.

```sh
pnpm android:profiling
```

### Android

The script [`android/app/build.gradle`](android/app/build.gradle) configures Ottrelite's Gradle plugin to inject tracing instrumentation for RN internals only for the `profiling` variant.

#### Debug build

```sh
pnpm android
```

#### Release build

```sh
pnpm android:release
```

#### Profiling build

```sh
pnpm android:profiling
```

### iOS

For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
Expand All @@ -54,13 +109,25 @@ bundle exec pod install

For more information, please visit [CocoaPods Getting Started guide](https://guides.cocoapods.org/using/getting-started.html).

#### Debug build

```sh
pnpm ios
```

If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.
#### Release build

```sh
pnpm ios:release
```

#### Profiling build

This is one way to run the app — you can also build it directly from Android Studio or Xcode.
```sh
pnpm ios:profiling
```

If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.

## Step 3: Trace the application

Expand Down
11 changes: 11 additions & 0 deletions examples/rn-app/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.callstack.ottrelite"
apply plugin: "com.facebook.react"

ottreliteTracing {
// only instrument RN internals tracing in profiling build variant
rnInstrumentationVariants = ["profiling"]
}

/**
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
Expand Down Expand Up @@ -98,6 +103,12 @@ android {
debug {
signingConfig signingConfigs.debug
}
profiling {
initWith release
matchingFallbacks = ["debug", "release"]
debuggable = true
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.callstack.plugin

import com.android.build.api.instrumentation.*
import com.android.build.api.instrumentation.AsmClassVisitorFactory
import com.android.build.api.instrumentation.ClassContext
import com.android.build.api.instrumentation.ClassData
import com.android.build.api.instrumentation.InstrumentationParameters
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.objectweb.asm.ClassVisitor
Expand Down Expand Up @@ -53,7 +56,7 @@ abstract class OttreliteClassVisitorFactory :
(access and Opcodes.ACC_STATIC) != 0

if (isTargetMethod && replacementDescriptor != null) {
println("Modifying method: $className.$name$descriptor")
println("[OttreliteInstrumentation] Modifying method: $className.$name$descriptor")

// Return a new MethodVisitor that will replace the method's body.
return replacementDescriptor.methodVisitorType.getMethodVisitor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,82 @@ import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.api.variant.AndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.SetProperty

open class OttreliteTracingExtension(project: Project) {
/**
* Set of variant names where tracing should be enabled.
* If empty, tracing will be applied to all variants.
*
* Example usage in build.gradle:
* ```
* ottreliteTracing {
* rnInstrumentationVariants = ["debug", "profiling"]
* }
* ```
*/
val rnInstrumentationVariants: SetProperty<String> =
project.objects.setProperty(String::class.java)
.convention(emptySet())
}

class OttreliteTracingPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create(
"ottreliteTracing",
OttreliteTracingExtension::class.java,
project
)

val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)

extension.rnInstrumentationVariants.get().let { instrumentedVariantsWhitelist ->
val description =
if (instrumentedVariantsWhitelist.isEmpty()) {
"all variants"
} else {
"variants: ${instrumentedVariantsWhitelist.joinToString(", ")}"
}
project.logger.lifecycle(
"[OttreliteTracing] Configuration will instrument $description"
)
}

androidComponents.onVariants { variant ->
variant.instrumentation.transformClassesWith(
OttreliteClassVisitorFactory::class.java,
InstrumentationScope.ALL
) { params ->
params.targetClassName.set("com.facebook.systrace.Systrace")
params.timestamp.set(System.currentTimeMillis())
val variantName = variant.name
val shouldInstrument = shouldInstrumentVariant(variantName, extension)

if (shouldInstrument) {
project.logger.lifecycle("[OttreliteTracing] Enabling instrumentation for variant '$variantName'")

variant.instrumentation.transformClassesWith(
OttreliteClassVisitorFactory::class.java,
InstrumentationScope.ALL
) { params ->
params.targetClassName.set("com.facebook.systrace.Systrace")
params.timestamp.set(System.currentTimeMillis())
}
variant.instrumentation.setAsmFramesComputationMode(
FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
)
} else {
project.logger.lifecycle("[OttreliteTracing] Skipping instrumentation for variant '$variantName'")
}
variant.instrumentation.setAsmFramesComputationMode(
FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
)
}
}

private fun shouldInstrumentVariant(
variantName: String,
extension: OttreliteTracingExtension
): Boolean {
val rnInstrumentationVariants = extension.rnInstrumentationVariants.get()

// if enabled variants are specified, only instrument those
if (rnInstrumentationVariants.isNotEmpty()) {
return rnInstrumentationVariants.contains(variantName)
}

// otherwise, instrument all variants
return true
}
}
10 changes: 9 additions & 1 deletion examples/rn-app/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ module.exports = {
plugins: ['module:@ottrelite/instrumentation-react'],
env: {
production: {
plugins: ['react-native-paper/babel'],
plugins: [
'react-native-paper/babel',
[
'transform-inline-environment-variables',
{
include: ['DISABLE_TRACING'],
},
],
],
},
},
};
Loading