npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mukhy/mtn-telemetry-sdk

v2.2.4

Published

MTN Telemetry SDK for React and React Native applications

Readme

MTN Telemetry SDK

A lightweight OpenTelemetry wrapper for React and React Native applications. Collects traces, metrics, errors, and logs with minimal configuration and exports them to any OTLP-compatible backend (Elastic APM, Grafana, Jaeger, etc.).

Features

  • One-call initialization for React and React Native
  • Automatic instrumentation for:
    • Fetch requests (HTTP method, status, URL, duration)
    • React Navigation screen transitions
    • AppState foreground/background transitions
    • Console log/warn/error capture as OTLP log records
  • Custom business event tracking (trackEvent)
  • Async operation tracing with duration and error capture (trackOperation)
  • Manual error recording (recordError)
  • Global crash reporting for unhandled JS errors and promise rejections (installCrashReporting)
  • Multi-step business journey tracking (BusinessTracer)
  • Tracked UI component (TrackedPressable)
  • Protobuf (binary) OTLP encoding for Elastic APM compatibility
  • Debug mode with verbose OpenTelemetry diagnostic logging
  • Built-in MTN SRE metadata on every telemetry entry
  • TypeScript-ready with full type definitions

Requirements

| Dependency | Minimum version | | ------------ | --------------- | | React | >= 18.0.0 | | React Native | >= 0.72.0 |

Installation

npm install @mukhy/mtn-telemetry-sdk

or

yarn add @mukhy/mtn-telemetry-sdk

Quick Start

Create src/telemetry.ts:

import { MTNOTel, type OTelRNOptions } from '@mukhy/mtn-telemetry-sdk';
import { Platform } from 'react-native';
import DeviceInfo from 'react-native-device-info';

export async function initTelemetry(
  navigationRef?: OTelRNOptions['navigationRef'],
) {
  const options: OTelRNOptions = {
    serviceName: `myApp-${Platform.OS === 'ios' ? 'iOS' : 'Android'}`,
    environment: 'production',
    debug: false,
    navigationRef,
    samplingRatio: 1,
    release: DeviceInfo.getVersion(),
    enableConsole: true,
    consoleMinSeverity: 'log',
    otlp: {
      tracesUrl: 'https://your-apm-server:8200/v1/traces',
      metricsUrl: 'https://your-apm-server:8200/v1/metrics',
      logsUrl: 'https://your-apm-server:8200/v1/logs',
      headers: {},
    },
  };

  return MTNOTel.init(options);
}

2. Initialize in your root component

In App.tsx:

import React, { useEffect, useRef } from 'react';
import { initTelemetry } from './telemetry';
import { installCrashReporting } from '@mukhy/mtn-telemetry-sdk';
import { navigationRef } from './routes/navigationRef';

function App() {
  const telemetryRef = useRef(null);

  useEffect(() => {
    let uninstallCrash;

    initTelemetry(navigationRef.current)
      .then(instance => {
        telemetryRef.current = instance;
        uninstallCrash = installCrashReporting();
        console.log('Telemetry SDK + crash reporting initialized');
      })
      .catch(err => console.warn('Telemetry init failed', err));

    return () => {
      uninstallCrash?.();
      telemetryRef.current?.flushAndShutdown();
    };
  }, []);

  return (
    <NavigationContainer ref={navigationRef}>
      {/* ... your app */}
    </NavigationContainer>
  );
}

That's it. Fetch requests, screen transitions, AppState changes, unhandled errors, promise rejections, and console output are now automatically captured.


Configuration Options (OTelRNOptions)

| Option | Type | Default | Description | | -------------------- | ------------------------- | -------------------- | ---------------------------------------------------------------------- | | serviceName | string | (required) | Service name in the OpenTelemetry resource. | | environment | string | undefined | Deployment environment tag (e.g. production, staging). | | release | string | undefined | Release/build version identifier. | | debug | boolean | false | When true, enables verbose OTel diagnostic logging to the console. | | otlp | object | undefined | OTLP exporter configuration. | | otlp.tracesUrl | string | http://localhost:4318 | OTLP HTTP endpoint for traces. | | otlp.metricsUrl | string | undefined | OTLP HTTP endpoint for metrics. | | otlp.logsUrl | string | undefined | OTLP HTTP endpoint for logs. | | otlp.headers | Record<string, string> | {} | Custom headers for every OTLP export request (auth, etc.). | | enableFetch | boolean | true | Toggle automatic fetch instrumentation. | | enableNavigation | boolean | true | Toggle React Navigation instrumentation. | | enableAppState | boolean | true | Toggle AppState foreground/background spans and metrics. | | enableConsole | boolean | false | Capture console.log/warn/error and export as OTLP log records. | | consoleMinSeverity | 'log' \| 'warn' \| 'error' | 'log' | Minimum console severity to capture (when enableConsole is true). | | navigationRef | NavigationContainerLike | undefined | Navigation container ref to attach during init. | | samplingRatio | number | 1.0 | Trace sampling ratio (0.01.0). | | attributes | Record<string, any> | {} | Extra resource attributes merged into the OTel resource. |


API Reference

trackEvent(name, attributes?, isError?)

Record a discrete business event as a span. The span is created and immediately ended.

import { trackEvent } from '@mukhy/mtn-telemetry-sdk';

// Successful event
trackEvent('purchase.completed', {
  'purchase.product_name': '75MB Daily Plan',
  'purchase.amount': 75,
  'purchase.transaction_id': 'TXN-12345',
  'purchase.payment_method': 'Airtime',
});

// Error event (sets span status to ERROR)
trackEvent('purchase.failed', {
  'purchase.product_name': '75MB Daily Plan',
  'purchase.error': 'Insufficient balance',
}, true);

| Param | Type | Description | | ------------ | ----------------------------------------- | ------------------------------------------ | | name | string | Event/span name. | | attributes | Record<string, string\|number\|boolean> | Key-value pairs attached to the span. | | isError | boolean | If true, marks the span status as ERROR. |


trackOperation(name, attributes, fn)

Wrap an async function and measure it as a span. Captures duration, result, and any thrown errors automatically.

import { trackOperation } from '@mukhy/mtn-telemetry-sdk';

const result = await trackOperation(
  'auth.login_with_otp',
  { 'auth.method': 'sms', 'auth.phone': '+234...' },
  () => AuthService.loginWithSMS(phone, otp),
);

| Param | Type | Description | | ------------ | ----------------------------------------- | ------------------------------------------ | | name | string | Operation/span name. | | attributes | Record<string, string\|number\|boolean> | Key-value pairs attached to the span. | | fn | () => Promise<T> | The async function to execute and measure. |

Returns: The resolved value of fn(). If fn() throws, the error is recorded on the span and re-thrown.


recordError(error, attributes?)

Manually record a caught error as a traced span with stack trace.

import { recordError } from '@mukhy/mtn-telemetry-sdk';

try {
  await riskyOperation();
} catch (err) {
  recordError(err, { screen: 'PaymentSummary', step: 'subscription_api' });
}

| Param | Type | Description | | ------------ | ----------------------------------------- | -------------------------------------------- | | error | unknown | The error or exception to record. | | attributes | Record<string, string\|number\|boolean> | Extra context attached to the error span. |


installCrashReporting()

Install global handlers that automatically capture:

  • Unhandled JavaScript errors (via React Native's ErrorUtils)
  • Unhandled promise rejections

Call once after MTNOTel.init(). Returns a cleanup function.

import { installCrashReporting } from '@mukhy/mtn-telemetry-sdk';

const uninstall = installCrashReporting();

// On unmount:
uninstall();

BusinessTracer

Track multi-step user journeys (e.g. onboarding, purchase flow).

import { BusinessTracer } from '@mukhy/mtn-telemetry-sdk';

BusinessTracer.start('PurchaseFlow', { 'bundle.category': 'DataPlan' });
BusinessTracer.step('BundleSelected', { 'bundle.name': '75MB Daily Plan' });
BusinessTracer.step('PaymentAuthorized', { 'payment.method': 'Airtime' });
BusinessTracer.complete({ 'purchase.status': 'success', 'transaction.id': 'TXN-123' });

| Method | Parameters | Description | | ---------- | ----------------------------- | --------------------------------- | | start | (journeyName, attributes?) | Begin a named journey. | | step | (stepName, attributes?) | Record a step within the journey. | | complete | (attributes?) | Mark the journey as complete. |


TrackedPressable

A drop-in replacement for React Native's Pressable that automatically records user taps as business steps.

import { TrackedPressable } from '@mukhy/mtn-telemetry-sdk';

<TrackedPressable
  accessibilityLabel="Buy Now"
  onPress={() => handleBuyNow()}
>
  <Text>Buy Now</Text>
</TrackedPressable>

Each press creates a tap:Buy Now business step span.


startActiveSpan(name, fn, kind?)

Create a custom active span for full manual control. The span is automatically ended and errors are captured.

import { startActiveSpan } from '@mukhy/mtn-telemetry-sdk';

const data = await startActiveSpan('fetchUserProfile', async (span) => {
  span.setAttribute('user.id', userId);
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
});

Built-in Instrumentations

| Instrumentation | What it captures | | -------------------- | ----------------------------------------------------------------------------------------- | | Fetch | Every fetch() call: method, URL, status code, duration. OTLP export URLs auto-excluded. | | React Navigation | Screen transitions: route name, duration on each screen. | | AppState | Foreground/background transitions as spans and metrics. | | Console | console.log/warn/error output as OTLP log records with trace context correlation. |


Built-in Metadata

Every telemetry entry automatically includes the following resource attributes:

| Field (Kibana) | Value | Description | | ----------------------- | -------------------------- | -------------------------------- | | labels.agentName | mtn-apm-agent | Agent identifier | | labels.maintainer | mtn-sre-team | Maintaining team | | labels.coreMaintainer | [email protected]| Core maintainer contact | | service.name | e.g. myMTNNG-Android | Service name from config | | service.environment | e.g. production | Environment from config | | service.version | App version | From DeviceInfo.getVersion() | | labels.device_model | e.g. sdk_gphone64_arm64 | Device model | | labels.device_os | e.g. Android 16 | OS name and version | | labels.app_bundle | e.g. ng.mtn.nextgen | App bundle ID | | labels.app_build | Build number | App build number | | labels.app_isEmulator | true / false | Whether running on emulator |


Elastic APM Notes

Protobuf Encoding

The SDK uses protobuf (binary) OTLP encoding and XMLHttpRequest + Blob for reliable binary transport in React Native. This is required for Elastic APM servers that only accept application/x-protobuf payloads.

Authentication

If your Elastic APM server requires authentication:

otlp: {
  tracesUrl: 'http://your-apm-server:8200/v1/traces',
  headers: {
    Authorization: 'Basic ' + btoa('elastic:your-password'),
  },
},

Android Cleartext HTTP

If your APM server uses http:// (not https://), add the server's IP to your Android network security config:

<!-- android/app/src/main/res/xml/network_security_config.xml -->
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">10.1.153.199</domain>
  </domain-config>
</network-security-config>

What Shows Up in Elastic APM

| Data Type | Source | Kibana Index | Example | | ------------------ | --------------------------- | ------------------ | --------------------------------------------- | | Transactions | Auto fetch instrumentation | traces-apm* | HTTP GET https://api.example.com/v1/data | | Transactions | Auto navigation | traces-apm* | Screen: HomeScreen | | Spans | trackEvent | traces-apm* | purchase.completed, bundle.selected | | Spans | trackOperation | traces-apm* | auth.login_with_otp (with duration) | | Errors | recordError | logs-apm.error* | Caught exceptions with stack trace | | Errors | installCrashReporting | logs-apm.error* | Unhandled JS errors and promise rejections | | Errors | Auto fetch instrumentation | logs-apm.error* | Network request failures | | Metrics | AppState instrumentation | Metrics index | App foreground/background time | | Logs | Console instrumentation | logs-apm.app* | All console.log/warn/error output |

Kibana KQL Queries

| What | Query | | -------------------------- | --------------------------------------------------------------------------------------- | | All business events | service.framework.name: "mtn-sdk:events" | | All SDK errors | service.framework.name: "mtn-sdk:errors" | | Login attempts | transaction.name: "auth.login_with_otp" | | Bundle selections | transaction.name: "bundle.selected" | | Purchase completed | transaction.name: "purchase.completed" | | Purchase failed | transaction.name: "purchase.failed" | | Crash reports | labels.error_source: "globalHandler" OR labels.error_source: "unhandledRejection" | | All SDK data | labels.agentName: "mtn-apm-agent" | | Filter out dev noise | NOT url.domain: "10.0.2.2" AND NOT url.domain: "play.google.com" |


Shutdown

MTNOTel.init() returns a singleton instance with a flushAndShutdown() method that flushes any pending telemetry and cleans up resources.

const telemetry = await MTNOTel.init({ serviceName: 'my-app' });

// When the app is closing or during tests:
await telemetry.flushAndShutdown();

Publishing

The SDK is published to npm as @mukhy/mtn-telemetry-sdk.

# Bump version in package.json, then:
npm run prepare   # cleans and builds
npm publish

Contributing

  1. Fork the repo
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Commit your changes
  4. Push and open a PR