Measure logo

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.

OptionTypeDefaultDescription
enableLoggingBooleanfalseTurn on internal SDK logs.
autoStartBooleantrueStart tracking automatically on init. Set to false to delay starting collection.
maxDiskUsageInMbInt50Cap the disk space used for buffered data. Clamped between 20MB and 1500MB.
trackActivityIntentDataBooleanfalseCapture the intent data used to launch an Activity.
requestHeadersProviderMsrRequestHeadersProvider?nullAdd custom HTTP headers to requests the SDK sends to the Measure API, useful for self-hosted setups.
enableFullCollectionModeBooleanfalseOverride all sampling and collect every event and trace. Increases cost, so use it for debugging only.
enableDiagnosticModeBooleanfalseWrite all SDK logs to a file you can attach when reporting an SDK bug.
OptionTypeDefaultDescription
enableLoggingBoolfalseTurn on internal SDK logs.
autoStartBooltrueStart tracking automatically on init. Set to false to delay starting collection.
maxDiskUsageInMbInt50Cap the disk space used for buffered data. Clamped between 20MB and 1500MB.
requestHeadersProviderMsrRequestHeadersProvider?nilAdd custom HTTP headers to requests the SDK sends to the Measure API, useful for self-hosted setups.
enableFullCollectionModeBoolfalseOverride all sampling and collect every event and trace. Increases cost, so use it for debugging only.
enableDiagnosticModeBoolfalseWrite all SDK logs to a file you can attach when reporting an SDK bug.
enableDiagnosticModeGestureBoolfalseExport SDK logs with a two-finger double-tap share sheet. Requires enableDiagnosticMode.
OptionTypeDefaultDescription
enableLoggingboolfalseTurn on internal SDK logs.
autoStartbooltrueStart tracking automatically on init. Set to false to delay starting collection.
enableDiagnosticModeboolfalseWrite all SDK logs to a file you can attach when reporting an SDK bug.
widgetFilterMap<Type, String>built-in setWidget types to include in layout snapshots. Generate a fuller list with the measure_build package.
OptionTypeDefaultDescription
enableLoggingbooleanfalseTurn on internal SDK logs.
autoStartbooleantrueStart tracking automatically on init. Set to false to delay starting collection.
enableDiagnosticModebooleanfalseWrite all SDK logs to a file you can attach when reporting an SDK bug.
patchIdstring?noneUUID 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.
patchVersionstring?noneHuman-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 issue