SDK API Reference
Public API reference for the Measure SDK, with per-platform code examples for Android, iOS, Flutter, React Native and Kotlin Multiplatform.
Initialize the SDK
Initialize the SDK with init before calling any other method. Call it as early as possible in app startup so crashes, errors and events are captured from the beginning.
import sh.measure.android.Measure
import sh.measure.android.config.MeasureConfig
Measure.init(this, MeasureConfig())import Measure
let clientInfo = ClientInfo(apiKey: "<apiKey>", apiUrl: "<apiUrl>")
Measure.initialize(with: clientInfo, config: BaseMeasureConfig())import 'package:measure_flutter/measure_flutter.dart';
await Measure.instance.init(
() => runApp(MeasureWidget(child: MyApp())),
config: const MeasureConfig(),
);import { Measure, MeasureConfig } from '@measuresh/react-native';
const config = new MeasureConfig({});
await Measure.init({ config });Kotlin Multiplatform has no shared init. Initialize each native SDK in its entry point, as shown in the Android and iOS tabs.
SDK configuration options
Pass a config object to init to customize the SDK. The available options differ by platform.
| Option | Type | Default | Description |
|---|---|---|---|
enableLogging | Boolean | false | Turn on internal SDK logs. |
autoStart | Boolean | true | Start tracking automatically on init. Set to false to delay starting collection. |
maxDiskUsageInMb | Int | 50 | Cap the disk space used for buffered data. Clamped between 20MB and 1500MB. |
trackActivityIntentData | Boolean | false | Capture the intent data used to launch an Activity. |
requestHeadersProvider | MsrRequestHeadersProvider? | null | Add custom HTTP headers to requests the SDK sends to the Measure API, useful for self-hosted setups. |
enableFullCollectionMode | Boolean | false | Override all sampling and collect every event and trace. Increases cost, so use it for debugging only. |
enableDiagnosticMode | Boolean | false | Write all SDK logs to a file you can attach when reporting an SDK bug. |
| Option | Type | Default | Description |
|---|---|---|---|
enableLogging | Bool | false | Turn on internal SDK logs. |
autoStart | Bool | true | Start tracking automatically on init. Set to false to delay starting collection. |
maxDiskUsageInMb | Int | 50 | Cap the disk space used for buffered data. Clamped between 20MB and 1500MB. |
requestHeadersProvider | MsrRequestHeadersProvider? | nil | Add custom HTTP headers to requests the SDK sends to the Measure API, useful for self-hosted setups. |
enableFullCollectionMode | Bool | false | Override all sampling and collect every event and trace. Increases cost, so use it for debugging only. |
enableDiagnosticMode | Bool | false | Write all SDK logs to a file you can attach when reporting an SDK bug. |
enableDiagnosticModeGesture | Bool | false | Export SDK logs with a two-finger double-tap share sheet. Requires enableDiagnosticMode. |
| Option | Type | Default | Description |
|---|---|---|---|
enableLogging | bool | false | Turn on internal SDK logs. |
autoStart | bool | true | Start tracking automatically on init. Set to false to delay starting collection. |
enableDiagnosticMode | bool | false | Write all SDK logs to a file you can attach when reporting an SDK bug. |
widgetFilter | Map<Type, String> | built-in set | Widget types to include in layout snapshots. Generate a fuller list with the measure_build package. |
| Option | Type | Default | Description |
|---|---|---|---|
enableLogging | boolean | false | Turn on internal SDK logs. |
autoStart | boolean | true | Start tracking automatically on init. Set to false to delay starting collection. |
enableDiagnosticMode | boolean | false | Write all SDK logs to a file you can attach when reporting an SDK bug. |
patchId | string? | none | UUID of the current over-the-air (OTA) patch. Auto-detected with the Metro plugin. Set it manually for OTA systems that bypass Metro, like CodePush. |
patchVersion | string? | none | Human-readable label for the OTA patch, like v1.0.3-hotfix. |
Kotlin Multiplatform has no shared config. Configure each native SDK directly, as shown in the Android and iOS tabs.
Start tracking
The SDK starts collecting data automatically after init. If you set autoStart to false in the config, call start when you're ready to begin tracking.
import sh.measure.android.Measure
Measure.start()import Measure
Measure.start()import 'package:measure_flutter/measure_flutter.dart';
await Measure.instance.start();import { Measure } from '@measuresh/react-native';
await Measure.start();import sh.measure.kmp.Measure
Measure.start()Stop tracking
Pause data collection with stop. While stopped, the SDK collects no data. Call start to resume.
import sh.measure.android.Measure
Measure.stop()import Measure
Measure.stop()import 'package:measure_flutter/measure_flutter.dart';
await Measure.instance.stop();import { Measure } from '@measuresh/react-native';
await Measure.stop();import sh.measure.kmp.Measure
Measure.stop()Track errors
You can report errors you catch and recover from. These don't crash the app but often point to problems worth fixing. Crashes and ANRs are captured automatically, so you don't need to track those yourself.
Track a handled error
import sh.measure.android.Measure
try {
methodThatThrows()
} catch (e: Exception) {
Measure.trackHandledException(e)
}import Measure
// Track a Swift Error or an NSError with trackError
do {
try someThrowingFunction()
} catch {
Measure.trackError(error)
}import 'package:measure_flutter/measure_flutter.dart';
try {
methodThatThrows();
} catch (e, stackTrace) {
Measure.instance.trackHandledError(e, stackTrace);
}import { Measure } from '@measuresh/react-native';
try {
methodThatThrows();
} catch (e) {
Measure.trackError({ error: e });
}import sh.measure.kmp.Measure
try {
methodThatThrows()
} catch (e: Exception) {
Measure.trackHandledException(e)
}Add attributes
See Attribute limits for allowed keys and values.
import sh.measure.android.Measure
import sh.measure.android.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("screen", "Login").build()
Measure.trackHandledException(e, attributes)import Measure
Measure.trackError(error, attributes: ["screen": .string("Login")])import 'package:measure_flutter/measure_flutter.dart';
final attributes = AttributeBuilder().add("screen", "Login").build();
Measure.instance.trackHandledError(e, stackTrace, attributes: attributes);import { Measure } from '@measuresh/react-native';
Measure.trackError({ error: e, attributes: { screen: "Login" } });import sh.measure.kmp.Measure
import sh.measure.kmp.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("screen", "Login").build()
Measure.trackHandledException(e, attributes)Track custom events
Track app-specific events like user actions or feature usage with trackEvent.
- Event names can be up to 64 characters.
- Event names can contain only letters, numbers, hyphens and underscores.
Track an event
import sh.measure.android.Measure
Measure.trackEvent("event_name")import Measure
Measure.trackEvent(name: "event_name", attributes: [:])import 'package:measure_flutter/measure_flutter.dart';
Measure.instance.trackEvent(name: "event_name");import { Measure } from '@measuresh/react-native';
Measure.trackEvent({ name: "event_name" });import sh.measure.kmp.Measure
Measure.trackEvent(name = "event_name")Set a custom timestamp
Record an event at a specific time, in milliseconds since epoch. Use getCurrentTime for an accurate monotonic value.
import sh.measure.android.Measure
Measure.trackEvent("event_name", timestamp = Measure.getCurrentTime())import Measure
Measure.trackEvent(name: "event_name", attributes: [:], timestamp: Measure.getCurrentTime())import 'package:measure_flutter/measure_flutter.dart';
Measure.instance.trackEvent(name: "event_name", timestamp: Measure.instance.getCurrentTime());import { Measure } from '@measuresh/react-native';
Measure.trackEvent({ name: "event_name", timestamp: Measure.getCurrentTime() });import sh.measure.kmp.Measure
Measure.trackEvent(name = "event_name", timestamp = Measure.getCurrentTime())Add attributes
See Attribute limits for allowed keys and values.
import sh.measure.android.Measure
import sh.measure.android.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("is_premium_user", true).build()
Measure.trackEvent("event_name", attributes = attributes)import Measure
Measure.trackEvent(name: "event_name", attributes: ["is_premium_user": .boolean(true)])import 'package:measure_flutter/measure_flutter.dart';
final attributes = AttributeBuilder().add("is_premium_user", true).build();
Measure.instance.trackEvent(name: "event_name", attributes: attributes);import { Measure } from '@measuresh/react-native';
Measure.trackEvent({ name: "event_name", attributes: { is_premium_user: true } });import sh.measure.kmp.Measure
import sh.measure.kmp.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("is_premium_user", true).build()
Measure.trackEvent(name = "event_name", attributes = attributes)Track screen views
The SDK automatically tracks screen views from each platform's navigation system. Record a screen from a custom navigation setup with trackScreenView.
Track a screen view
import sh.measure.android.Measure
Measure.trackScreenView("Home")import Measure
Measure.trackScreenView("Home", attributes: nil)import 'package:measure_flutter/measure_flutter.dart';
Measure.instance.trackScreenViewEvent(name: "Home");import { Measure } from '@measuresh/react-native';
Measure.trackScreenView({ screenName: "Home" });import sh.measure.kmp.Measure
Measure.trackScreenView("Home")Add attributes
See Attribute limits for allowed keys and values.
import sh.measure.android.Measure
import sh.measure.android.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("source", "deep_link").build()
Measure.trackScreenView("Home", attributes)import Measure
Measure.trackScreenView("Home", attributes: ["source": .string("deep_link")])import 'package:measure_flutter/measure_flutter.dart';
final attributes = AttributeBuilder().add("source", "deep_link").build();
Measure.instance.trackScreenViewEvent(name: "Home", attributes: attributes);import { Measure } from '@measuresh/react-native';
Measure.trackScreenView({ screenName: "Home", attributes: { source: "deep_link" } });import sh.measure.kmp.Measure
import sh.measure.kmp.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("source", "deep_link").build()
Measure.trackScreenView("Home", attributes)Automatic tracking on Flutter
Add MsrNavigatorObserver to your app's navigatorObservers to track screen views automatically. It works best with named routes.
import 'package:flutter/material.dart';
import 'package:measure_flutter/measure_flutter.dart';
MaterialApp(
navigatorObservers: [MsrNavigatorObserver()],
home: HomeScreen(),
);Track performance traces
Measure how long any operation takes with a span. A span represents one unit of work. Set a parent to trace a multi-step flow.
- Span names can be up to 64 characters.
- Span names cannot be empty.
Start a span
Start a span immediately with startSpan.
import sh.measure.android.Measure
val span = Measure.startSpan("span-name")import Measure
let span = Measure.startSpan(name: "span-name")import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name");import { Measure } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "span-name" });import sh.measure.kmp.Measure
val span = Measure.startSpan("span-name")Start with a timestamp
Trace an operation that already started by passing a start time from getCurrentTime, which returns epoch time from a monotonic clock.
import sh.measure.android.Measure
val span = Measure.startSpan("span-name", timestamp = Measure.getCurrentTime())import Measure
let span = Measure.startSpan(name: "span-name", timestamp: Measure.getCurrentTime())import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name", timestamp: Measure.instance.getCurrentTime());import { Measure } from '@measuresh/react-native';
const span = Measure.startSpanWithTimestamp({ name: "span-name", timestampMs: Measure.getCurrentTime() });import sh.measure.kmp.Measure
val span = Measure.startSpan("span-name", timestamp = Measure.getCurrentTime())End a span
End a span with end. Set the status before ending.
import sh.measure.android.Measure
import sh.measure.android.tracing.SpanStatus
val span = Measure.startSpan("span-name")
span.setStatus(SpanStatus.Ok).end()import Measure
let span = Measure.startSpan(name: "span-name")
span.setStatus(.ok).end()import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name");
span.setStatus(SpanStatus.ok).end();import { Measure, SpanStatus } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "span-name" });
span.setStatus(SpanStatus.Ok).end();import sh.measure.kmp.Measure
import sh.measure.kmp.tracing.SpanStatus
val span = Measure.startSpan("span-name")
span.setStatus(SpanStatus.Ok).end()End with a timestamp
End a span that already finished by passing an end time from getCurrentTime.
import sh.measure.android.Measure
import sh.measure.android.tracing.SpanStatus
val span = Measure.startSpan("span-name")
span.setStatus(SpanStatus.Ok).end(timestamp = Measure.getCurrentTime())import Measure
let span = Measure.startSpan(name: "span-name")
span.setStatus(.ok).end(timestamp: Measure.getCurrentTime())import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name");
span.setStatus(SpanStatus.ok).end(timestamp: Measure.instance.getCurrentTime());import { Measure, SpanStatus } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "span-name" });
span.setStatus(SpanStatus.Ok).end(Measure.getCurrentTime());import sh.measure.kmp.Measure
import sh.measure.kmp.tracing.SpanStatus
val span = Measure.startSpan("span-name")
span.setStatus(SpanStatus.Ok).end(timestamp = Measure.getCurrentTime())Set the status
Set the outcome of the operation with setStatus. Values are Ok, Error and Unset (the default).
import sh.measure.android.Measure
import sh.measure.android.tracing.SpanStatus
val span = Measure.startSpan("span-name")
span.setStatus(SpanStatus.Ok)import Measure
let span = Measure.startSpan(name: "span-name")
span.setStatus(.ok)import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name");
span.setStatus(SpanStatus.ok);import { Measure, SpanStatus } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "span-name" });
span.setStatus(SpanStatus.Ok);import sh.measure.kmp.Measure
import sh.measure.kmp.tracing.SpanStatus
val span = Measure.startSpan("span-name")
span.setStatus(SpanStatus.Ok)Set a parent
Build a hierarchy of operations by setting a parent span with setParent.
import sh.measure.android.Measure
val parent = Measure.startSpan("parent-span")
val child = Measure.startSpan("child-span").setParent(parent)import Measure
let parent = Measure.startSpan(name: "parent-span")
let child = Measure.startSpan(name: "child-span").setParent(parent)import 'package:measure_flutter/measure_flutter.dart';
final parent = Measure.instance.startSpan("parent-span");
final child = Measure.instance.startSpan("child-span").setParent(parent);import { Measure } from '@measuresh/react-native';
const parent = Measure.startSpan({ name: "parent-span" });
const child = Measure.startSpan({ name: "child-span" }).setParent(parent);import sh.measure.kmp.Measure
val parent = Measure.startSpan("parent-span")
val child = Measure.startSpan("child-span").setParent(parent)Add attributes
Attach key-value context to a span. Add one attribute at a time, several at once with setAttributes, or remove one with removeAttribute. See Attribute limits for allowed keys and values.
import sh.measure.android.Measure
import sh.measure.android.attributes.AttributesBuilder
val span = Measure.startSpan("span-name")
span.setAttribute("key", "value")
span.setAttribute("count", 10)
val attributes = AttributesBuilder().put("key", "value").put("count", 10).build()
span.setAttributes(attributes)
span.removeAttribute("key")import Measure
let span = Measure.startSpan(name: "span-name")
span.setAttribute("key", value: "value")
span.setAttribute("count", value: 10)
let attributes: [String: AttributeValue] = ["key": .string("value"), "count": .int(10)]
span.setAttributes(attributes)
span.removeAttribute("key")import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name");
span.setAttributeString("key", "value");
span.setAttributeInt("count", 10);
span.setAttributeDouble("ratio", 10.5);
span.setAttributeBool("enabled", true);
final attributes = AttributeBuilder().add("key", "value").add("count", 10).build();
span.setAttributes(attributes);
span.removeAttribute("key");import { Measure } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "span-name" });
span.setAttribute("key", "value");
span.setAttribute("count", 10);
span.setAttributes({ key: "value", count: 10, enabled: true });
span.removeAttribute("key");import sh.measure.kmp.Measure
import sh.measure.kmp.attributes.AttributesBuilder
val span = Measure.startSpan("span-name")
span.setAttribute("key", "value")
span.setAttribute("count", 10)
val attributes = AttributesBuilder().put("key", "value").put("count", 10).build()
span.setAttributes(attributes)
span.removeAttribute("key")Rename a span
Update a span's name after it starts with setName.
import sh.measure.android.Measure
val span = Measure.startSpan("span-name")
span.setName("updated-name")import Measure
let span = Measure.startSpan(name: "span-name")
span.setName("updated-name")import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name");
span.setName("updated-name");import { Measure } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "span-name" });
span.setName("updated-name");import sh.measure.kmp.Measure
val span = Measure.startSpan("span-name")
span.setName("updated-name")Add a checkpoint
Mark a significant moment during a span with setCheckpoint. A span can hold up to 100 checkpoints.
import sh.measure.android.Measure
val span = Measure.startSpan("span-name")
span.setCheckpoint("checkpoint-name")import Measure
let span = Measure.startSpan(name: "span-name")
span.setCheckpoint("checkpoint-name")import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("span-name");
span.setCheckpoint("checkpoint-name");import { Measure } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "span-name" });
span.setCheckpoint("checkpoint-name");import sh.measure.kmp.Measure
val span = Measure.startSpan("span-name")
span.setCheckpoint("checkpoint-name")Defer a span
Configure a span now and start it later with createSpanBuilder.
import sh.measure.android.Measure
val builder = Measure.createSpanBuilder("span-name")
val span = builder?.startSpan()import Measure
let builder = Measure.createSpanBuilder(name: "span-name")
let span = builder?.startSpan()import 'package:measure_flutter/measure_flutter.dart';
final builder = Measure.instance.createSpanBuilder("span-name");
final span = builder?.startSpan();import { Measure } from '@measuresh/react-native';
const builder = Measure.createSpanBuilder({ name: "span-name" });
const span = builder?.startSpan();import sh.measure.kmp.Measure
val builder = Measure.createSpanBuilder("span-name")
val span = builder?.startSpan()Distributed tracing
Propagate a trace across services by adding the W3C traceparent header to outgoing requests. Get the header key with getTraceParentHeaderKey and its value for a span with getTraceParentHeaderValue.
import sh.measure.android.Measure
val span = Measure.startSpan("http")
val key = Measure.getTraceParentHeaderKey()
val value = Measure.getTraceParentHeaderValue(span)import Measure
let span = Measure.startSpan(name: "http")
let key = Measure.getTraceParentHeaderKey()
let value = Measure.getTraceParentHeaderValue(span: span)import 'package:measure_flutter/measure_flutter.dart';
final span = Measure.instance.startSpan("http");
final key = Measure.instance.getTraceParentHeaderKey();
final value = Measure.instance.getTraceParentHeaderValue(span);import { Measure } from '@measuresh/react-native';
const span = Measure.startSpan({ name: "http" });
const key = Measure.getTraceParentHeaderKey();
const value = Measure.getTraceParentHeaderValue({ span });import sh.measure.kmp.Measure
val span = Measure.startSpan("http")
val key = Measure.getTraceParentHeaderKey()
val value = Measure.getTraceParentHeaderValue(span)Track HTTP events
Measure automatically tracks OkHttp on Android and, with the measure_dio package, Dio on Flutter. Use trackHttpEvent to record requests from any other HTTP client. Use getCurrentTime for the start and end time to avoid clock skew.
import sh.measure.android.Measure
val startTime = Measure.getCurrentTime()
// make the request
val endTime = Measure.getCurrentTime()
Measure.trackHttpEvent(
url = "https://api.example.com/users",
method = "GET",
startTime = startTime,
endTime = endTime,
statusCode = 200,
)import Measure
let startTime = UInt64(Measure.getCurrentTime())
// make the request
let endTime = UInt64(Measure.getCurrentTime())
Measure.trackHttpEvent(
url: "https://api.example.com/users",
method: "GET",
startTime: startTime,
endTime: endTime,
statusCode: 200
)import 'package:measure_flutter/measure_flutter.dart';
final startTime = Measure.instance.getCurrentTime();
// make the request
final endTime = Measure.instance.getCurrentTime();
Measure.instance.trackHttpEvent(
url: "https://api.example.com/users",
method: HttpMethod.get,
startTime: startTime,
endTime: endTime,
statusCode: 200,
);import { Measure } from '@measuresh/react-native';
const startTime = Measure.getCurrentTime();
// make the request
const endTime = Measure.getCurrentTime();
Measure.trackHttpEvent({
url: "https://api.example.com/users",
method: "GET",
startTime,
endTime,
statusCode: 200,
});import sh.measure.kmp.Measure
val startTime = Measure.getCurrentTime()
// make the request
val endTime = Measure.getCurrentTime()
Measure.trackHttpEvent(
url = "https://api.example.com/users",
method = "GET",
startTime = startTime,
endTime = endTime,
statusCode = 200,
)Track bug reports
Let users report bugs from inside the app. Open the bug report UI with a single call, or submit a report from your own UI with trackBugReport. Descriptions can be up to 4000 characters, with a maximum of 5 attachments. Attach a screenshot with captureScreenshot, and trigger reports on device shake.
Open bug report screen
import sh.measure.android.Measure
Measure.launchBugReportActivity(takeScreenshot = true)import Measure
Measure.launchBugReport(takeScreenshot: true)Show the bug report widget, typically pushed as a new screen:
import 'package:flutter/material.dart';
import 'package:measure_flutter/measure_flutter.dart';
Navigator.of(context).push(
MaterialPageRoute<Widget>(
builder: (context) => Measure.instance.createBugReportWidget(),
),
);import { Measure } from '@measuresh/react-native';
Measure.launchBugReport({ takeScreenshot: true });import sh.measure.kmp.Measure
Measure.launchBugReport(takeScreenshot = true)Track a bug report
Build a custom bug report flow and submit it with trackBugReport.
import sh.measure.android.Measure
Measure.trackBugReport(description = "Cart items disappear after reopening the app")import Measure
Measure.trackBugReport(description: "Cart items disappear after reopening the app")import 'package:measure_flutter/measure_flutter.dart';
Measure.instance.trackBugReport(
description: "Cart items disappear after reopening the app",
attachments: [],
attributes: {},
);import { Measure } from '@measuresh/react-native';
Measure.trackBugReport({ description: "Cart items disappear after reopening the app" });import sh.measure.kmp.Measure
Measure.trackBugReport(description = "Cart items disappear after reopening the app")Add attributes
Attach metadata to a report. Pass attributes when you open the bug report UI or track a report from your own UI. See Attribute limits for allowed keys and values.
import sh.measure.android.Measure
import sh.measure.android.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("screen", "Cart").build()
Measure.launchBugReportActivity(takeScreenshot = true, attributes = attributes)
Measure.trackBugReport(description = "...", attributes = attributes)import Measure
let attributes: [String: AttributeValue] = ["screen": .string("Cart")]
Measure.launchBugReport(takeScreenshot: true, attributes: attributes)
Measure.trackBugReport(description: "...", attributes: attributes)import 'package:measure_flutter/measure_flutter.dart';
final attributes = AttributeBuilder().add("screen", "Cart").build();
Measure.instance.createBugReportWidget(attributes: attributes);
Measure.instance.trackBugReport(description: "...", attachments: [], attributes: attributes);import { Measure } from '@measuresh/react-native';
Measure.launchBugReport({ takeScreenshot: true, attributes: { screen: "Cart" } });
Measure.trackBugReport({ description: "...", attributes: { screen: "Cart" } });import sh.measure.kmp.Measure
import sh.measure.kmp.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("screen", "Cart").build()
Measure.launchBugReport(takeScreenshot = true, attributes = attributes)
Measure.trackBugReport(description = "...", attributes = attributes)Shake to report
Register a shake handler so users can open a bug report by shaking their device. Pass a null handler to disable it.
import sh.measure.android.Measure
import sh.measure.android.bugreport.MsrShakeListener
Measure.setShakeListener(object : MsrShakeListener {
override fun onShake() {
Measure.launchBugReportActivity()
}
})import Measure
Measure.onShake {
Measure.launchBugReport()
}import 'package:measure_flutter/measure_flutter.dart';
Measure.instance.setShakeListener(() {
// open the bug report widget
});import { Measure } from '@measuresh/react-native';
Measure.onShake({ handler: () => {
Measure.launchBugReport();
} });Shake to report isn't part of Kotlin Multiplatform's shared API.
Track logs
Record a log at one of five severity levels, using logDebug, logInfo, logWarning, logError or logFatal. Logs appear on the session timeline and add context when debugging. Bodies longer than 1000 characters are truncated.
Track a log
import sh.measure.android.Measure
Measure.logDebug("Cache miss for key user_42")
Measure.logInfo("User signed in")
Measure.logWarning("Payment failed, retrying")
Measure.logError("Checkout request failed")
Measure.logFatal("Unrecoverable database error")import Measure
Measure.logDebug("Cache miss for key user_42")
Measure.logInfo("User signed in")
Measure.logWarning("Payment failed, retrying")
Measure.logError("Checkout request failed")
Measure.logFatal("Unrecoverable database error")import 'package:measure_flutter/measure_flutter.dart';
Measure.instance.logDebug("Cache miss for key user_42");
Measure.instance.logInfo("User signed in");
Measure.instance.logWarning("Payment failed, retrying");
Measure.instance.logError("Checkout request failed");
Measure.instance.logFatal("Unrecoverable database error");import { Measure } from '@measuresh/react-native';
Measure.logDebug({ body: "Cache miss for key user_42" });
Measure.logInfo({ body: "User signed in" });
Measure.logWarning({ body: "Payment failed, retrying" });
Measure.logError({ body: "Checkout request failed" });
Measure.logFatal({ body: "Unrecoverable database error" });import sh.measure.kmp.Measure
Measure.logDebug("Cache miss for key user_42")
Measure.logInfo("User signed in")
Measure.logWarning("Payment failed, retrying")
Measure.logError("Checkout request failed")
Measure.logFatal("Unrecoverable database error")Add attributes
See Attribute limits for allowed keys and values.
import sh.measure.android.Measure
import sh.measure.android.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("screen", "Checkout").build()
Measure.logWarning("Payment failed", attributes)import Measure
Measure.logWarning("Payment failed", attributes: ["screen": .string("Checkout")])import 'package:measure_flutter/measure_flutter.dart';
final attributes = AttributeBuilder().add("screen", "Checkout").build();
Measure.instance.logWarning("Payment failed", attributes: attributes);import { Measure } from '@measuresh/react-native';
Measure.logWarning({ body: "Payment failed", attributes: { screen: "Checkout" } });import sh.measure.kmp.Measure
import sh.measure.kmp.attributes.AttributesBuilder
val attributes = AttributesBuilder().put("screen", "Checkout").build()
Measure.logWarning("Payment failed", attributes)Identify users
Set a user ID to correlate sessions with a user when debugging. The ID persists across app launches. Clear it when the user logs out.
Avoid personally identifiable information (PII) like email or phone number in the user ID. Use a hashed or anonymized value instead.
import sh.measure.android.Measure
Measure.setUserId("user-id")
Measure.clearUserId()import Measure
Measure.setUserId("user-id")
Measure.clearUserId()import 'package:measure_flutter/measure_flutter.dart';
await Measure.instance.setUserId("user-id");
await Measure.instance.clearUserId();import { Measure } from '@measuresh/react-native';
Measure.setUserId({ userId: "user-id" });
Measure.clearUserId();import sh.measure.kmp.Measure
Measure.setUserId("user-id")
Measure.clearUserId()Get session ID
Read the current session ID to correlate app data with a Measure session. Returns null if the SDK isn't initialized.
import sh.measure.android.Measure
val sessionId: String? = Measure.getSessionId()import Measure
let sessionId: String? = Measure.getSessionId()import 'package:measure_flutter/measure_flutter.dart';
final String? sessionId = await Measure.instance.getSessionId();import { Measure } from '@measuresh/react-native';
const sessionId: string | null = await Measure.getSessionId();import sh.measure.kmp.Measure
val sessionId: String? = Measure.getSessionId()Get current time
Read epoch time in milliseconds from a monotonic clock. Use it for event, span and HTTP timestamps to avoid clock skew.
import sh.measure.android.Measure
val currentTime: Long = Measure.getCurrentTime()import Measure
let currentTime: Int64 = Measure.getCurrentTime()import 'package:measure_flutter/measure_flutter.dart';
final int currentTime = Measure.instance.getCurrentTime();import { Measure } from '@measuresh/react-native';
const currentTime: number = Measure.getCurrentTime();import sh.measure.kmp.Measure
val currentTime: Long = Measure.getCurrentTime()Mask SwiftUI views
Screenshots mask all SwiftUI content by default, whatever the mask level, because SwiftUI views can't be inspected individually. Reveal a view that isn't sensitive with .msrUnmask(), or force masking with .msrMask() for a view automatic detection misses, like a standalone Text outside a List.
import Measure
VStack {
Text("Order confirmed")
.msrUnmask()
Text(cardNumber)
.msrMask()
}Mask Flutter widgets
Screenshots mask Flutter text, input and image widgets automatically based on the mask level, and a TextField with obscureText or a password, email or phone keyboard type is always masked. Wrap anything automatic detection misses, like a custom-painted widget showing sensitive data, with MsrMask to always redact its area regardless of the mask level.
import 'package:measure_flutter/measure_flutter.dart';
MsrMask(
child: AccountBalance(amount: balance),
)Attribute limits
Attributes are the key-value pairs you attach to events, spans, logs, bug reports, screen views and errors. They follow the same rules everywhere:
- Keys are strings, up to 256 characters.
- Keys can contain only letters, numbers, hyphens and underscores.
- Values are a string, integer, long, double, float or boolean. On React Native, values are a string, number or boolean.
- String values can be up to 256 characters.
Think this page can be better?
Open an issueMCP Server
Connect Measure to Claude Code, OpenAI Codex, Google Antigravity, Cursor and other AI coding agents. Query crashes, traces, sessions and bug reports from your editor or AI agent workflows.
Overview
Measure's REST APIs for auth, event ingestion and data fetching, designed around standard REST principles.