App Health Pings

pub package License: MIT

Low-overhead heartbeats and basic device info to your backend. Helps catch broken builds and environment drift early.

Features

  • 🔄 Automatic Health Pings: Periodic heartbeats to monitor app health
  • 📱 Device Information: Collect platform, OS version, device model, and connectivity status
  • 🎯 Smart Backoff: Exponential backoff on failures with configurable limits
  • 🔄 Lifecycle Aware: Automatic pings on app start and resume
  • 🛠 Configurable: Flexible configuration for intervals, endpoints, and data collection
  • 🔒 Privacy Focused: Optional device info collection with granular control
  • 📦 Lightweight: Minimal dependencies and overhead
  • 🌐 Network Aware: Only sends pings when connectivity is available
  • 🏷 Tagging Support: Custom tags for environment, version, and deployment tracking

Getting Started

Add this to your package's pubspec.yaml file:

dependencies:
  app_health_pings: ^1.0.0

Usage

Basic Setup

import 'package:app_health_pings/app_health_pings.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize health pings
  await AppHealthPings.initialize(
    AppHealthConfig(
      endpoint: Uri.parse('https://your-api.com/health-pings'),
      authToken: 'your-bearer-token', // Optional
      interval: const Duration(minutes: 15),
      enabled: true,
    ),
  );
  
  runApp(MyApp());
}

Advanced Configuration

await AppHealthPings.initialize(
  AppHealthConfig(
    endpoint: Uri.parse('https://your-api.com/health-pings'),
    authToken: 'bearer-token-here',
    tags: {
      'env': 'production',
      'version': '1.0.0',
      'deployment': 'us-east-1',
    },
    interval: const Duration(minutes: 10),
    maxInterval: const Duration(minutes: 30),
    enabled: true,
    sendOnStart: true,
    sendOnResume: true,
    collectDeviceInfo: true,
    connectTimeout: const Duration(seconds: 10),
  ),
);

Manual Pings

// Send a manual ping
await AppHealthPings.pingNow(reason: 'user_action');

// Send ping with extra data
await AppHealthPings.pingNow(
  reason: 'feature_usage',
  extra: {
    'feature': 'payment_completed',
    'amount': 99.99,
    'user_id': 'user_123',
  },
);

Heartbeat Control

// Start automatic heartbeats
AppHealthPings.startHeartbeat();

// Stop automatic heartbeats
AppHealthPings.stopHeartbeat();

Payload Structure

Your backend will receive POST requests with the following JSON structure:

{
  "timestamp": "2024-01-15T10:30:00.000Z",
  "reason": "interval",
  "installId": "550e8400-e29b-41d4-a716-446655440000",
  "sessionId": "550e8400-e29b-41d4-a716-446655440001",
  "package": {
    "name": "com.example.myapp",
    "version": "1.0.0",
    "buildNumber": "1"
  },
  "connectivity": ["wifi"],
  "device": {
    "platform": "android",
    "androidSdk": 33,
    "manufacturer": "Google",
    "model": "Pixel 7",
    "isPhysicalDevice": true
  },
  "tags": {
    "env": "production",
    "version": "1.0.0"
  },
  "extra": {}
}

Device Information by Platform

Android:

  • platform: "android"
  • androidSdk: API level
  • manufacturer: Device manufacturer
  • model: Device model
  • isPhysicalDevice: true/false

iOS:

  • platform: "ios"
  • systemName: "iOS"
  • systemVersion: iOS version
  • model: Device identifier
  • isPhysicalDevice: true/false

Other Platforms:

  • platform: Platform name (web, windows, linux, macos)

Configuration Options

Option Type Default Description
endpoint Uri Required Your backend endpoint URL
authToken String? null Optional Bearer token for authentication
tags Map<String, String>? null Custom tags sent with every ping
interval Duration 15 minutes Base interval between automatic pings
maxInterval Duration 1 hour Maximum interval during exponential backoff
enabled bool true Master switch to enable/disable all pings
sendOnStart bool true Send ping when app starts
sendOnResume bool true Send ping when app resumes from background
collectDeviceInfo bool true Include device information in pings
connectTimeout Duration 5 seconds HTTP request timeout

Backend Setup

Your backend should:

  1. Accept POST requests to the configured endpoint
  2. Expect Content-Type: application/json
  3. Handle optional Authorization: Bearer <token> header
  4. Return HTTP 200-299 status for success
  5. Handle the x-apphealth-schema: 1 header

Example Express.js Handler

app.post('/health-pings', (req, res) => {
  const ping = req.body;
  
  console.log('Health ping received:', {
    timestamp: ping.timestamp,
    reason: ping.reason,
    platform: ping.device.platform,
    appVersion: ping.package.version,
    tags: ping.tags
  });
  
  // Store in database, send to monitoring service, etc.
  
  res.status(200).json({ success: true });
});

Use Cases

  • Build Monitoring: Detect when new builds stop sending pings
  • Environment Drift: Monitor device/OS distribution changes over time
  • App Health: Track app crashes or unexpected terminations
  • Deployment Verification: Ensure new releases are reaching users
  • User Engagement: Basic app usage analytics
  • Technical Monitoring: Network connectivity and performance insights

Privacy Considerations

  • Install and session IDs are generated fresh each app install/launch
  • Device information collection can be disabled via collectDeviceInfo: false
  • No personally identifiable information is collected by default
  • All data collection is transparent and configurable

Example App

See the example directory for a complete Flutter app demonstrating all features.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Author

Muzamil Ghafoor

License

This project is licensed under the MIT License - see the LICENSE file for details.

Copyright (c) 2024 Muzamil Ghafoor

Libraries

app_health_pings