react-native-qalink
v0.3.3
Published
Real-time error capture SDK for React Native — helps QA teams identify if bugs are frontend or backend
Maintainers
Readme
react-native-qalink 🔍
SDK de captura de errores en tiempo real para React Native. Se instala en la app y transmite al servidor QALink cada request de red, error JS, log de consola y error del runtime — permitiendo al equipo de QA ver en tiempo real si un bug es del frontend o del backend, sin necesidad de Android Studio ni logs técnicos.
¿Cómo identifica los requests a la API?
La librería usa dos estrategias de interceptación simultáneas:
1. Monkey-patch de fetch global
Al llamar QALink.init(), la librería reemplaza el fetch nativo del entorno JavaScript por su propia versión instrumentada. Cualquier llamada a fetch(...) en toda la app pasa primero por QALink, que registra el request y la respuesta antes de devolver el control al código original.
Tu código → fetch('/api/users')
↓
QALink intercepta → registra método, URL, tiempo de inicio
↓
Ejecuta fetch real → espera respuesta del servidor
↓
QALink registra → status code, body, duración, clasificación
↓
Devuelve al código → response (sin modificarla)2. Interceptores de Axios
Axios expone un sistema oficial de interceptores (.interceptors.request y .interceptors.response). QALink se engancha en ambos para capturar cada request antes de que salga y cada respuesta antes de que llegue al código de la app. Funciona con la instancia global y con instancias personalizadas.
¿Cómo captura los logs de Metro y el runtime de React Native?
Hay tres capas adicionales que se activan automáticamente con init():
3. Monkey-patch de console
QALink reemplaza console.log, console.warn y console.error con versiones instrumentadas. Metro sigue mostrando todo normalmente (se llama al original primero), pero cada mensaje se envía también al dashboard. Los mensajes se clasifican automáticamente:
console.log('Productos cargados', data) → 🔵 user_log
console.warn('componentWillMount deprecated') → 🟡 rn_warning
console.error('Invariant Violation: ...') → 🔴 rn_error
console.error('Unable to resolve module') → 🔴 rn_error (Metro)4. ErrorUtils.setGlobalHandler — Pantalla roja (Red Screen)
React Native expone ErrorUtils para capturar errores antes de que la app crashee y muestre la pantalla roja. QALink se registra ahí para enviar el error al dashboard con todo el stack trace, y luego llama al handler original para que RN siga comportándose normalmente.
Error fatal en JS
↓
QALink captura → envía al dashboard con stack trace completo
↓
ErrorUtils original → React Native muestra la pantalla roja5. LogBox — Cajas amarillas (Yellow Box)
En React Native ≥ 0.64, los warnings se manejan internamente via LogBox. QALink se engancha al método interno __warn de LogBox para capturar cada warning antes de que aparezca en pantalla.
Clasificación de errores de runtime
| Tipo | Categoría | Ejemplo |
|--------------------------|----------------|----------------------------------------------|
| Error fatal | red_screen | Invariant Violation, undefined is not... |
| Warning de RN | yellow_box | componentWillMount is deprecated |
| Módulo nativo | native_module| NativeModule X is null |
| Bridge JS↔Native | bridge | RCTBridge error |
| Promise sin .catch() | unknown | Unhandled Promise Rejection |
| Error de Metro/bundler | rn_error | Unable to resolve module, SyntaxError |
Instalación
npm install react-native-qalink
# o
yarn add react-native-qalinkEjemplos de uso
Ejemplo 1 — Setup básico con captura de consola
// App.tsx
import { useEffect } from 'react';
import { QALink } from 'react-native-qalink';
import axios from 'axios';
export default function App() {
useEffect(() => {
QALink.init({
serverUrl: 'ws://192.168.1.100:3000',
apiKey: 'qlk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appVersion: '1.2.3',
captureRuntimeErrors: true, // pantalla roja, yellow box
console: {
captureLogs: true, // console.log
captureWarnings: true, // console.warn
captureErrors: true, // console.error + errores de Metro
},
debug: __DEV__,
});
QALink.interceptAxios(axios);
}, []);
return <RootNavigator />;
}Ejemplo 2 — Filtrar logs muy verbosos de librerías
Si usas librerías que loguean mucho (react-query, redux, etc.), puedes filtrarlas para que el dashboard no se llene de ruido.
QALink.init({
serverUrl: 'ws://192.168.1.100:3000',
apiKey: 'qlk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appVersion: '1.2.3',
console: {
captureLogs: true,
captureWarnings: true,
captureErrors: true,
// Ignorar logs de estas librerías
ignorePatterns: [
'[react-query]',
'[redux]',
'Reanimated',
'Gesture Handler',
'[MMKV]',
],
},
});Ejemplo 3 — Capturar solo logs relevantes para QA
Si quieres que el QA vea únicamente los logs que tú marcas explícitamente:
QALink.init({
serverUrl: 'ws://192.168.1.100:3000',
apiKey: 'qlk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appVersion: '1.2.3',
console: {
captureLogs: true,
captureWarnings: true,
captureErrors: true,
// Solo capturar logs que contengan estas palabras
includePatterns: ['[QA]', '[FLOW]', '[ERROR]'],
},
});
// En tu código, prefija los logs importantes:
console.log('[QA] Usuario llegó al checkout', { userId, cartTotal });
console.log('[FLOW] Pago procesado', { orderId });
console.error('[ERROR] Falló validación', { campo, valor });Ejemplo 4 — App con múltiples instancias de Axios
// services/api.ts
export const apiPrincipal = axios.create({ baseURL: 'https://api.miapp.com/v1' });
export const apiPagos = axios.create({ baseURL: 'https://pagos.miapp.com' });
// App.tsx
await QALink.init({
serverUrl: 'ws://192.168.1.100:3000',
apiKey: 'qlk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appVersion: '1.2.3',
});
QALink.interceptAxios(apiPrincipal);
QALink.interceptAxios(apiPagos);
// fetch nativo ya se intercepta automáticamente con init()Ejemplo 5 — Integración con React Navigation
<NavigationContainer
ref={navigationRef}
onStateChange={() => {
const routeName = navigationRef.current?.getCurrentRoute()?.name ?? '';
QALink.setScreen(routeName);
// Genera automáticamente: breadcrumb "NAVIGATE → CheckoutScreen"
}}
>Ejemplo 6 — Breadcrumbs en acciones críticas
Los breadcrumbs + logs de consola juntos dan la línea de tiempo completa al QA:
const handleConfirmarCompra = async () => {
QALink.addBreadcrumb('TAP → Confirmar Compra', {
cartId: cart.id,
items: cart.items.length,
total: cart.total,
});
console.log('[QA] Iniciando checkout', { cartId: cart.id });
try {
const result = await apiPagos.post('/checkout', { cartId: cart.id });
console.log('[QA] Checkout exitoso', { orderId: result.data.orderId });
} catch (error) {
// QALink ya capturó el error de red automáticamente
// console.error aquí también se envía al dashboard
console.error('[QA] Checkout falló', error);
}
};El QA verá en el dashboard esta línea de tiempo completa:
14:33:40 NAVIGATE → CheckoutScreen
14:33:55 TAP → Confirmar Compra { cartId: "abc", items: 3, total: 150 }
14:33:55 🔵 [QA] Iniciando checkout { cartId: "abc" }
14:33:55 ❌ POST /checkout → 500 🔴 ERROR DE BACKEND
14:33:55 🔴 [QA] Checkout falló Error: Internal Server ErrorEjemplo 7 — Configuración para build de QA (sin exponer en producción)
// config/qalink.ts
import Config from 'react-native-config';
export async function initQALink() {
if (Config.QALINK_ENABLED !== 'true') return;
await QALink.init({
serverUrl: Config.QALINK_SERVER,
apiKey: Config.QALINK_API_KEY,
appVersion: Config.VERSION_NAME ?? '0.0.0',
logNetworkBodies: true,
captureRuntimeErrors: true,
console: {
captureLogs: true,
captureWarnings: true,
captureErrors: true,
ignorePatterns: ['[react-query]'],
},
sensitiveHeaders: ['Authorization'],
sensitiveUrlPatterns: ['/auth/refresh'],
});
QALink.interceptAxios(axios);
}Ejemplo 8 — Verificar estado de conexión
function QAStatusBadge() {
if (QALink.getStatus() !== 'connected') return null;
return (
<View style={styles.badge}>
<Text>🔴 QALink grabando</Text>
</View>
);
}Qué ve el QA en el dashboard
Cada sesión incluye una línea de tiempo unificada con todos los eventos:
| Ícono | Tipo | Fuente | |-------|-------------------|---------------------| | ✅ | Request exitoso | fetch / axios | | ❌ | Error de red | fetch / axios | | 🔴 | Error fatal (RN) | ErrorUtils / LogBox | | 🟡 | Warning (RN) | LogBox / console | | 🔵 | Log de consola | console.log | | 📍 | Breadcrumb | QALink.addBreadcrumb| | 📱 | Pantalla | QALink.setScreen |
Configuración completa
QALink.init({
// — Requeridos —
serverUrl: 'ws://192.168.1.100:3000',
apiKey: 'qlk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appVersion: '1.0.0',
// — Red —
logNetworkBodies: false,
sensitiveHeaders: ['Authorization'],
sensitiveUrlPatterns: ['/auth/refresh'],
// — Runtime de React Native —
captureRuntimeErrors: true, // pantalla roja, yellow box, promises
// — Consola y Metro —
console: {
captureLogs: true,
captureWarnings: true,
captureErrors: true,
ignorePatterns: ['[redux]'], // patrones a ignorar
includePatterns: [], // si se define, solo captura estos
},
// — General —
enabled: true,
debug: false,
onEvent: (event) => {
console.log(event.type, event);
},
});API completa
| Método | Descripción |
|---------------------------------|-----------------------------------------------------------|
| QALink.init(config) | Inicializa el SDK, intercepta fetch, consola y runtime |
| QALink.interceptAxios(instance)| Registra una instancia de axios |
| QALink.setScreen(name) | Registra la pantalla actual (genera breadcrumb) |
| QALink.addBreadcrumb(action, data?) | Registra una acción del usuario |
| QALink.getStatus() | Estado de la conexión WS |
| QALink.destroy() | Limpia interceptores y desconecta |
