Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions e2e/react-native-otel/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Image, StyleSheet, Alert, Pressable } from 'react-native'
import { useState, useEffect } from 'react'
import axios from 'axios'

import { HelloWave } from '@/components/HelloWave'
import ParallaxScrollView from '@/components/ParallaxScrollView'
Expand Down Expand Up @@ -463,6 +464,29 @@ export default function HomeScreen() {
}, 0)
}

const handleAxiosSuccess = async () => {
await axios.get('https://jsonplaceholder.typicode.com/posts/1')
Alert.alert('Axios Request', `Request completed`)
}

const handleAxios404 = async () => {
try {
await axios.get('https://jsonplaceholder.typicode.com/posts/99999')
} catch (error) {
Alert.alert('Axios 404', `Request completed`)
throw error
}
}

const handleAxios500 = async () => {
try {
await axios.get('https://httpstatuses.maor.io/500')
} catch (error) {
Alert.alert('Axios 500', `Request completed`)
throw error
}
}

return (
<ParallaxScrollView
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
Expand Down Expand Up @@ -528,6 +552,37 @@ export default function HomeScreen() {
</ThemedText>
</Pressable>

<ThemedText type="subtitle" style={{ marginTop: 16 }}>
Axios Network Requests
</ThemedText>

<Pressable
style={[styles.button, styles.successButton]}
onPress={handleAxiosSuccess}
>
<ThemedText style={styles.buttonText}>
Axios: Successful Request
</ThemedText>
</Pressable>

<Pressable
style={[styles.button, styles.warningButton]}
onPress={handleAxios404}
>
<ThemedText style={styles.buttonText}>
Axios: 404 Request
</ThemedText>
</Pressable>

<Pressable
style={[styles.button, styles.errorButton]}
onPress={handleAxios500}
>
<ThemedText style={styles.buttonText}>
Axios: 500 Request
</ThemedText>
</Pressable>

<ThemedText type="subtitle" style={{ marginTop: 16 }}>
Error Testing
</ThemedText>
Expand Down Expand Up @@ -639,6 +694,12 @@ const styles = StyleSheet.create({
marginVertical: 4,
alignItems: 'center',
},
successButton: {
backgroundColor: '#34C759', // Green color for success buttons
},
warningButton: {
backgroundColor: '#FF9500', // Orange color for warning/404 buttons
},
errorButton: {
backgroundColor: '#FF3B30', // Red color for error buttons
},
Expand Down
87 changes: 0 additions & 87 deletions e2e/react-native-otel/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,93 +44,6 @@ export default function RootLayout() {
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
})

// Add global error handlers directly in the React component
useEffect(() => {
// Direct error handler for promise rejections
const errorHandler = (error: any) => {
console.log('[RootLayout] Caught unhandled error:', error)
if (error instanceof Error) {
LDObserve.recordError(error, {
'error.unhandled': true,
'error.caught_by': 'root_component_handler',
})
}
}

// Register a direct listener for unhandled rejections at the app level
const rejectionHandler = (event: any) => {
const error = event.reason || new Error('Unknown promise rejection')
console.log(
'[RootLayout] Caught unhandled promise rejection:',
error,
)
LDObserve.recordError(
error instanceof Error ? error : new Error(String(error)),
{
'error.unhandled': true,
'error.caught_by': 'root_component_promise_handler',
'promise.handled': false,
},
)
}

// Network error handler to catch fetch errors
const networkErrorHandler = (error: any) => {
console.log('[RootLayout] Caught network error:', error)
LDObserve.recordError(
error instanceof Error ? error : new Error(String(error)),
{
'error.unhandled': true,
'error.caught_by': 'root_component_network_handler',
'error.type': 'network',
},
)
}

// Set up the handlers
if (global.ErrorUtils) {
const originalGlobalHandler = global.ErrorUtils.getGlobalHandler()
global.ErrorUtils.setGlobalHandler((error, isFatal) => {
errorHandler(error)
if (originalGlobalHandler) {
originalGlobalHandler(error, isFatal)
}
})
}

// React Native doesn't fully support the standard addEventListener API for unhandledrejection
// This is a workaround using Promise patches
const originalPromiseReject = Promise.reject
Promise.reject = function (reason) {
const result = originalPromiseReject.call(this, reason)
setTimeout(() => {
// If the rejection isn't handled in the next tick, report it
if (!result._handled) {
rejectionHandler({ reason })
}
}, 0)
return result
}

// Patch fetch to catch network errors
const originalFetch = global.fetch
global.fetch = function (...args) {
return originalFetch.apply(this, args).catch((error) => {
networkErrorHandler(error)
throw error // re-throw to preserve original behavior
})
} as typeof fetch

return () => {
// Cleanup if component unmounts (unlikely for root layout)
if (global.ErrorUtils && originalGlobalHandler) {
global.ErrorUtils.setGlobalHandler(originalGlobalHandler)
}
Promise.reject = originalPromiseReject
global.fetch = originalFetch
}
}, [])

useEffect(() => {
if (loaded) {
SplashScreen.hideAsync()
Expand Down
1 change: 1 addition & 0 deletions e2e/react-native-otel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6",
"axios": "^1.12.2",
"expo": "~53.0.13",
"expo-blur": "~14.1.5",
"expo-constants": "^17.1.6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"devDependencies": {
"@launchdarkly/observability-shared": "workspace:*",
"@launchdarkly/react-native-client-sdk": "^10.0.0",
"axios": "^1.12.2",
"react-native": "^0.79.0",
"typedoc": "^0.28.4",
"typescript": "^5.0.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { vi } from 'vitest'

export const Platform = {
OS: 'ios' as const,
select: (config: any) => config.ios || config.default,
Expand Down Expand Up @@ -28,3 +30,9 @@ export default {
DeviceEventEmitter,
NativeEventEmitter,
}

// Mock global ErrorUtils (React Native global)
;(globalThis as any).ErrorUtils = {
getGlobalHandler: vi.fn(() => undefined),
setGlobalHandler: vi.fn(),
}
Loading
Loading