mixpanel-react-native
Version:
Official React Native Tracking Library for Mixpanel Analytics
509 lines (398 loc) • 13.5 kB
Markdown
# Workflow: Debugging Issues
## Overview
This workflow provides a systematic approach to debugging issues in the mixpanel-react-native library, covering both native and JavaScript implementation modes, common failure scenarios, and diagnostic techniques.
## 🔍 Initial Diagnostics
### Step 1: Determine Implementation Mode
```javascript
// Add this debug code to determine which mode is active
const debugImplementationMode = () => {
console.log('=== Mixpanel Debug Info ===');
console.log('Native module available:', !!NativeModules.MixpanelReactNative);
console.log('Implementation type:',
mixpanel.mixpanelImpl === MixpanelReactNative ? 'Native' : 'JavaScript');
console.log('Platform:', Platform.OS);
console.log('==============================');
};
```
### Step 2: Enable Comprehensive Logging
```javascript
// Enable logging for all tokens
mixpanel.setLoggingEnabled(true);
// Check current logging status
console.log('Logging enabled:', mixpanel.config?.getLoggingEnabled?.(token));
// Verify logs are appearing with [Mixpanel] prefix
// Expected output: [Mixpanel] Track 'Event Name' with properties {...}
```
### Step 3: Check Basic Configuration
```javascript
const debugConfiguration = async () => {
console.log('=== Configuration Debug ===');
console.log('Token:', mixpanel.token);
console.log('Track automatic events:', mixpanel.trackAutomaticEvents);
console.log('Opted out:', await mixpanel.hasOptedOutTracking());
console.log('Distinct ID:', await mixpanel.getDistinctId());
console.log('Device ID:', await mixpanel.getDeviceId());
console.log('Super properties:', await mixpanel.getSuperProperties());
console.log('==============================');
};
```
## 🔧 Native Mode Debugging
### Common Native Issues
#### Issue: "MixpanelReactNative is not available"
```bash
# iOS Troubleshooting
cd ios
pod install --repo-update
cd ..
# Verify podfile contains:
# pod 'MixpanelReactNative', :path => '../node_modules/mixpanel-react-native'
# Clean and rebuild
npx react-native run-ios --reset-cache
```
```bash
# Android Troubleshooting
cd android
./gradlew clean
./gradlew build
cd ..
# Verify autolinking worked
npx react-native config
# Clean and rebuild
npx react-native run-android --reset-cache
```
#### Issue: Native Method Calls Failing
```javascript
// Test native bridge directly
const testNativeBridge = async () => {
try {
// Test basic native method
await NativeModules.MixpanelReactNative?.initialize?.(
'test-token',
true,
false,
{},
'https://api.mixpanel.com',
false
);
console.log('✅ Native bridge working');
} catch (error) {
console.error('❌ Native bridge error:', error);
console.log('Available methods:', Object.keys(NativeModules.MixpanelReactNative || {}));
}
};
```
#### Issue: iOS-Specific Problems
```javascript
// Check iOS-specific features
const debugiOS = () => {
if (Platform.OS === 'ios') {
console.log('=== iOS Debug ===');
// Test iOS-specific methods
try {
mixpanel.setFlushOnBackground(true);
console.log('✅ iOS setFlushOnBackground working');
} catch (error) {
console.error('❌ iOS method error:', error);
}
console.log('==================');
}
};
```
#### Issue: Android-Specific Problems
```javascript
// Check Android-specific behavior
const debugAndroid = () => {
if (Platform.OS === 'android') {
console.log('=== Android Debug ===');
// Android doesn't support setFlushOnBackground
console.log('Flush on background not supported on Android');
// Check instance synchronization
console.log('Instance management: Thread-safe synchronized');
console.log('======================');
}
};
```
## 🖥️ JavaScript Mode Debugging
### Common JavaScript Issues
#### Issue: Events Not Being Tracked
```javascript
const debugEventTracking = async () => {
console.log('=== Event Tracking Debug ===');
// Check opt-out status first
const optedOut = await mixpanel.hasOptedOutTracking();
console.log('Opted out:', optedOut);
if (optedOut) {
console.log('❌ User has opted out - events will not be tracked');
return;
}
// Test queue addition
mixpanel.track('Debug Event', { debug: true, timestamp: Date.now() });
// Check if event was queued (in JavaScript mode)
if (mixpanel.mixpanelImpl !== MixpanelReactNative) {
console.log('✅ JavaScript mode - event should be queued');
// Note: Cannot directly access internal queue in production
}
console.log('=============================');
};
```
#### Issue: Queue Not Processing
```javascript
const debugQueueProcessing = () => {
console.log('=== Queue Processing Debug ===');
// Check flush interval
const config = mixpanel.mixpanelImpl?.config;
if (config) {
console.log('Flush interval:', config.getFlushInterval?.(mixpanel.token));
console.log('Batch size:', config.getFlushBatchSize?.(mixpanel.token));
console.log('Server URL:', config.getServerURL?.(mixpanel.token));
}
// Manual flush test
console.log('Triggering manual flush...');
mixpanel.flush();
console.log('===============================');
};
```
#### Issue: Storage Problems
```javascript
const debugStorage = async () => {
console.log('=== Storage Debug ===');
try {
// Test AsyncStorage directly
await AsyncStorage.setItem('mixpanel_test_key', 'test_value');
const value = await AsyncStorage.getItem('mixpanel_test_key');
console.log('✅ AsyncStorage working:', value === 'test_value');
await AsyncStorage.removeItem('mixpanel_test_key');
} catch (error) {
console.error('❌ AsyncStorage error:', error);
console.log('Falling back to in-memory storage');
}
// Test Mixpanel storage
try {
const originalProps = await mixpanel.getSuperProperties();
await mixpanel.registerSuperProperties({ debug_test: true });
const newProps = await mixpanel.getSuperProperties();
console.log('✅ Mixpanel storage working:', newProps.debug_test === true);
} catch (error) {
console.error('❌ Mixpanel storage error:', error);
}
console.log('===================');
};
```
## 🌐 Network Debugging
### Network Request Issues
```javascript
const debugNetwork = async () => {
console.log('=== Network Debug ===');
// Test basic connectivity
try {
const response = await fetch('https://api.mixpanel.com/track/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'data=' + encodeURIComponent(JSON.stringify([{
event: 'Debug Test',
properties: {
token: mixpanel.token,
time: Date.now(),
distinct_id: 'debug-user'
}
}]))
});
console.log('Network response status:', response.status);
const result = await response.json();
console.log('Network response body:', result);
if (response.status === 200 && result === 1) {
console.log('✅ Network connectivity working');
} else {
console.log('❌ Network issue detected');
}
} catch (error) {
console.error('❌ Network error:', error);
}
console.log('===================');
};
```
### Server URL Issues
```javascript
const debugServerURL = () => {
console.log('=== Server URL Debug ===');
const config = mixpanel.mixpanelImpl?.config;
if (config) {
const serverURL = config.getServerURL?.(mixpanel.token);
console.log('Current server URL:', serverURL);
// Test different server URLs
const validURLs = [
'https://api.mixpanel.com', // Default
'https://api-eu.mixpanel.com', // EU
];
validURLs.forEach(url => {
console.log(`Testing URL: ${url}`);
try {
mixpanel.setServerURL(url);
console.log(`✅ ${url} set successfully`);
} catch (error) {
console.error(`❌ ${url} failed:`, error);
}
});
}
console.log('========================');
};
```
## 🔄 Common Error Scenarios
### Scenario 1: Silent Failures
```javascript
const debugSilentFailures = () => {
console.log('=== Silent Failure Debug ===');
// Test with invalid data that might cause silent failures
const testCases = [
{ name: 'Circular reference', data: {} },
{ name: 'Function property', data: { fn: () => {} } },
{ name: 'Symbol property', data: { sym: Symbol('test') } },
{ name: 'Large object', data: { large: 'x'.repeat(10000) } },
];
// Create circular reference
testCases[0].data.self = testCases[0].data;
testCases.forEach(testCase => {
console.log(`Testing: ${testCase.name}`);
try {
JSON.stringify(testCase.data);
console.log(`✅ ${testCase.name} - serializable`);
mixpanel.track(`Test ${testCase.name}`, testCase.data);
console.log(`✅ ${testCase.name} - tracked successfully`);
} catch (error) {
console.error(`❌ ${testCase.name} - error:`, error.message);
}
});
console.log('============================');
};
```
### Scenario 2: Memory Leaks
```javascript
const debugMemoryUsage = async () => {
console.log('=== Memory Usage Debug ===');
const initialMemory = performance.memory?.usedJSHeapSize || 'Unknown';
console.log('Initial memory:', initialMemory);
// Create multiple instances to test cleanup
const instances = [];
for (let i = 0; i < 10; i++) {
const instance = new Mixpanel(`test-token-${i}`, true);
await instance.init();
instances.push(instance);
}
const afterCreateMemory = performance.memory?.usedJSHeapSize || 'Unknown';
console.log('Memory after creating 10 instances:', afterCreateMemory);
// Reset all instances
for (const instance of instances) {
await instance.reset();
}
const afterResetMemory = performance.memory?.usedJSHeapSize || 'Unknown';
console.log('Memory after reset:', afterResetMemory);
console.log('==========================');
};
```
### Scenario 3: Timing Issues
```javascript
const debugTimingIssues = async () => {
console.log('=== Timing Issues Debug ===');
// Test rapid initialization
const startTime = Date.now();
const instance = new Mixpanel('timing-test-token', true);
// Multiple rapid calls
const promises = [];
for (let i = 0; i < 5; i++) {
promises.push(instance.init());
}
try {
await Promise.all(promises);
const endTime = Date.now();
console.log(`✅ Multiple init calls completed in ${endTime - startTime}ms`);
} catch (error) {
console.error('❌ Timing issue detected:', error);
}
// Test rapid tracking
for (let i = 0; i < 100; i++) {
instance.track(`Rapid Event ${i}`, { index: i });
}
console.log('✅ Rapid tracking completed');
console.log('==========================');
};
```
## 🛠️ Diagnostic Utilities
### Complete Health Check
```javascript
const runCompleteHealthCheck = async () => {
console.log('🔍 Starting Mixpanel Health Check...\n');
try {
// 1. Implementation mode
debugImplementationMode();
// 2. Configuration
await debugConfiguration();
// 3. Storage
await debugStorage();
// 4. Network
await debugNetwork();
// 5. Event tracking
await debugEventTracking();
// 6. Platform-specific
if (Platform.OS === 'ios') {
debugiOS();
} else if (Platform.OS === 'android') {
debugAndroid();
}
console.log('✅ Health check completed');
} catch (error) {
console.error('❌ Health check failed:', error);
}
};
```
### Performance Monitor
```javascript
const monitorPerformance = () => {
const originalTrack = mixpanel.track.bind(mixpanel);
let trackCount = 0;
let totalTime = 0;
mixpanel.track = (eventName, properties) => {
const startTime = Date.now();
trackCount++;
const result = originalTrack(eventName, properties);
const endTime = Date.now();
const duration = endTime - startTime;
totalTime += duration;
console.log(`Track #${trackCount}: ${eventName} (${duration}ms)`);
console.log(`Average time per track: ${(totalTime / trackCount).toFixed(2)}ms`);
return result;
};
console.log('Performance monitoring enabled');
};
```
## 📊 Issue Resolution Checklist
### Basic Issues
- [ ] Verify implementation mode (native vs JavaScript)
- [ ] Check logging is enabled
- [ ] Confirm token is valid
- [ ] Verify user hasn't opted out
- [ ] Test basic connectivity
### Native Mode Issues
- [ ] Run `pod install` (iOS) or gradle clean/build (Android)
- [ ] Verify autolinking configuration
- [ ] Check native module availability
- [ ] Test native bridge directly
- [ ] Verify platform-specific features
### JavaScript Mode Issues
- [ ] Check AsyncStorage availability
- [ ] Verify queue processing
- [ ] Test manual flush
- [ ] Check storage persistence
- [ ] Monitor network requests
### Performance Issues
- [ ] Monitor memory usage
- [ ] Check for circular references
- [ ] Verify batch sizes
- [ ] Test under load
- [ ] Check timing/race conditions
### Data Issues
- [ ] Verify event structure
- [ ] Check super properties
- [ ] Test identity management
- [ ] Validate serialization
- [ ] Confirm server responses
This debugging workflow helps systematically identify and resolve issues across both implementation modes and all major subsystems.