Skip to content

Commit f5ac870

Browse files
committed
fix(mobile): integrate NewRelic monitoring with comprehensive logging
- Fix NewRelic initialization by accessing default export from module - Add NewRelic React Native Agent plugin to Expo config - Configure NewRelic Android Gradle plugin (v7.6.1) for native integration - Implement centralized logger with automatic user context tracking - Automatically includes context in all logs and events - Integrates with authentication flow for session management - Custom events for key user actions: - sign_in_success, sign_up_verification_sent, sign_up_complete - master_password_setup, master_password_unlocked - note_created, note_updated (with metadata)
1 parent a3445af commit f5ac870

File tree

12 files changed

+430
-67
lines changed

12 files changed

+430
-67
lines changed

apps/mobile/v1/app.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"expo": {
33
"name": "Typelets",
44
"slug": "typelets",
5-
"version": "1.22.5",
5+
"version": "1.22.6",
66
"orientation": "default",
77
"icon": "./assets/images/icon.png",
88
"scheme": "typelets",
@@ -58,6 +58,7 @@
5858
},
5959
"plugins": [
6060
"expo-router",
61+
"newrelic-react-native-agent",
6162
[
6263
"expo-splash-screen",
6364
{

apps/mobile/v1/index.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { APP_VERSION } from './src/constants/version';
33

44
// Initialize New Relic with error handling
55
try {
6-
const NewRelic = require('newrelic-react-native-agent');
6+
const NewRelicModule = require('newrelic-react-native-agent');
7+
const NewRelic = NewRelicModule.default || NewRelicModule;
78

89
if (NewRelic && NewRelic.startAgent) {
910
let appToken;
@@ -56,10 +57,21 @@ try {
5657
NewRelic.startAgent(appToken, agentConfiguration);
5758
NewRelic.setJSAppVersion(APP_VERSION);
5859

59-
console.log('[New Relic] Agent started successfully with version:', APP_VERSION);
60-
console.log('[New Relic] Platform:', Platform.OS);
60+
if (__DEV__) {
61+
console.log('[New Relic] Agent started successfully with version:', APP_VERSION);
62+
console.log('[New Relic] Platform:', Platform.OS);
63+
}
64+
65+
// Send initialization log to NewRelic
66+
if (NewRelic.logInfo) {
67+
NewRelic.logInfo('NewRelic agent initialized', {
68+
platform: Platform.OS,
69+
version: APP_VERSION,
70+
timestamp: new Date().toISOString(),
71+
});
72+
}
6173
} else {
62-
console.warn('[New Relic] Agent module not available');
74+
console.warn('[New Relic] Agent module loaded but startAgent not available');
6375
}
6476
} catch (error) {
6577
console.warn('[New Relic] Failed to initialize:', error.message);

apps/mobile/v1/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "v1",
33
"main": "index.js",
4-
"version": "1.22.5",
4+
"version": "1.22.6",
55
"scripts": {
66
"start": "expo start",
77
"reset-project": "node ./scripts/reset-project.js",

apps/mobile/v1/src/components/AppWrapper.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useTheme } from '../theme';
55
import AuthScreen from '../screens/AuthScreen';
66
import { MasterPasswordScreen } from './MasterPasswordDialog';
77
import { useMasterPassword } from '../hooks/useMasterPassword';
8+
import { logger } from '../lib/logger';
89

910
interface AppWrapperProps {
1011
children: React.ReactNode;
@@ -37,6 +38,30 @@ export const AppWrapper: React.FC<AppWrapperProps> = ({ children }) => {
3738
return () => clearTimeout(timer);
3839
}, [isLoading]);
3940

41+
// Track user authentication state in logger and New Relic
42+
useEffect(() => {
43+
if (isSignedIn && user?.id) {
44+
// User is signed in - set user ID and session attributes
45+
logger.setUserId(user.id);
46+
logger.setSessionAttributes({
47+
email: user.primaryEmailAddress?.emailAddress,
48+
username: user.username,
49+
isSignedIn: true,
50+
});
51+
52+
logger.info('User signed in', {
53+
attributes: {
54+
userId: user.id,
55+
email: user.primaryEmailAddress?.emailAddress,
56+
},
57+
});
58+
} else if (!isSignedIn) {
59+
// User signed out - clear session
60+
logger.info('User signed out');
61+
logger.clearSessionAttributes();
62+
}
63+
}, [isSignedIn, user?.id, user?.primaryEmailAddress?.emailAddress, user?.username]);
64+
4065
if (__DEV__) {
4166
console.log('AppWrapper Auth status:', {
4267
isSignedIn,

apps/mobile/v1/src/components/ErrorBoundary.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { Component, ErrorInfo, ReactNode } from 'react';
22
import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
33
import { SafeAreaView } from 'react-native-safe-area-context';
4+
import { logger } from '../lib/logger';
45

56
interface Props {
67
children: ReactNode;
@@ -32,19 +33,24 @@ class ErrorBoundary extends Component<Props, State> {
3233
}
3334

3435
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
35-
// Log error details in development
36-
if (__DEV__) {
37-
console.error('ErrorBoundary caught an error:', error, errorInfo);
38-
}
39-
4036
// Update state with error details
4137
this.setState({
4238
error,
4339
errorInfo,
4440
});
4541

46-
// Here you could also log to an error reporting service like Sentry
47-
// Example: Sentry.captureException(error, { contexts: { react: { componentStack: errorInfo.componentStack } } });
42+
// Log React error to NewRelic with component stack
43+
logger.error('React component error', error, {
44+
attributes: {
45+
componentStack: errorInfo.componentStack,
46+
errorBoundary: true,
47+
},
48+
});
49+
50+
// Log to console in development
51+
if (__DEV__) {
52+
console.error('ErrorBoundary caught an error:', error, errorInfo);
53+
}
4854
}
4955

5056
handleReset = () => {

apps/mobile/v1/src/hooks/useMasterPassword.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
unlockWithMasterPassword,
99
clearUserEncryptionData,
1010
} from '../lib/encryption';
11+
import { logger } from '../lib/logger';
1112

1213
// Global function to force all hook instances to refresh
1314
export function forceGlobalMasterPasswordRefresh() {
@@ -60,7 +61,11 @@ export function useMasterPassword() {
6061

6162
setLastCheckTime(Date.now());
6263
} catch (error) {
63-
if (__DEV__) console.error('Error checking master password status:', error);
64+
logger.error('Error checking master password status', error as Error, {
65+
attributes: {
66+
userId,
67+
},
68+
});
6469
// On error, assume needs setup
6570
setIsNewSetup(true);
6671
setNeedsUnlock(true);
@@ -99,12 +104,23 @@ export function useMasterPassword() {
99104
if (isNewSetup) {
100105
// Setting up new master password
101106
await setupMasterPassword(password, userId);
107+
logger.recordEvent('master_password_setup', {
108+
userId,
109+
});
102110
} else {
103111
// Unlocking with existing password
104112
const success = await unlockWithMasterPassword(password, userId);
105113
if (!success) {
114+
logger.warn('Invalid master password attempt', {
115+
attributes: {
116+
userId,
117+
},
118+
});
106119
throw new Error('Invalid password');
107120
}
121+
logger.recordEvent('master_password_unlocked', {
122+
userId,
123+
});
108124
}
109125

110126
// Successfully authenticated - update state
@@ -122,7 +138,11 @@ export function useMasterPassword() {
122138
try {
123139
await clearUserEncryptionData(userId);
124140
} catch (error) {
125-
if (__DEV__) console.error('Error clearing encryption data:', error);
141+
logger.error('Error clearing encryption data on sign out', error as Error, {
142+
attributes: {
143+
userId,
144+
},
145+
});
126146
}
127147
}
128148
};

apps/mobile/v1/src/lib/encryption/EncryptionService.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { encryptWithAESGCM, decryptWithAESGCM } from './core/aes';
1111
import { deriveEncryptionKey } from './core/keyDerivation';
1212
import { getUserSecret, getMasterKey, clearUserStorageData } from './storage/secureStorage';
1313
import { DecryptionCache } from './storage/cache';
14+
import { logger } from '../logger';
1415

1516
export class MobileEncryptionService {
1617
private cache: DecryptionCache;
@@ -161,9 +162,11 @@ export class MobileEncryptionService {
161162
* Clear all data for a user
162163
*/
163164
async clearUserData(userId: string): Promise<void> {
164-
if (__DEV__) {
165-
console.log('🗑️ Starting clearUserData for user:', userId);
166-
}
165+
logger.info('Clearing user encryption data', {
166+
attributes: {
167+
userId,
168+
},
169+
});
167170

168171
// Clear storage
169172
await clearUserStorageData(userId);
@@ -174,9 +177,11 @@ export class MobileEncryptionService {
174177
// Reset master password mode
175178
this.masterPasswordMode = false;
176179

177-
if (__DEV__) {
178-
console.log('✅ clearUserData completed');
179-
}
180+
logger.info('User encryption data cleared successfully', {
181+
attributes: {
182+
userId,
183+
},
184+
});
180185
}
181186

182187
/**

0 commit comments

Comments
 (0)