Skip to content

Flujo de desarrollo con Git

Guía de Envío de Logs para Frontend - GM Logs API

Objetivo de esta Guía

Esta guía está diseñada específicamente para desarrolladores frontend en React que necesitan integrar el sistema de logs de GM Transport en sus aplicaciones. Se enfoca exclusivamente en cómo ENVIAR logs al sistema, sin preocuparse por consultas o visualización (eso es trabajo del Dashboard).

¿Para qué sirve este sistema?

  • Centralizar todos los logs de tus aplicaciones React (web/móvil)
  • Rastrear errores de producción de forma estructurada
  • Debuggear problemas asociados a tickets específicos
  • Correlacionar eventos entre diferentes partes de tu aplicación
  • Adjuntar archivos de log o stack traces completos cuando es necesario

Tabla de Contenidos

  1. Conceptos Fundamentales
  2. Niveles de Log y Cuándo Usarlos
  3. Campos del Modelo de Log
  4. Endpoints Disponibles (POST)
  5. Mejores Prácticas
  6. Casos de Uso Prácticos
  7. Sistema de Tickets
  8. Correlation ID y Trazabilidad
  9. Manejo de Errores
  10. Ejemplos Completos

Conceptos Fundamentales

¿Qué es un Log?

Un log es un registro estructurado de un evento que ocurrió en tu aplicación. Piensa en él como una fotografía de un momento específico que incluye:

  • Qué pasó (mensaje)
  • Cuándo pasó (timestamp automático)
  • Dónde pasó (source, application)
  • Quién lo causó (userId, rfc, sessionId)
  • Contexto adicional (metadata, tags)

Arquitectura del Sistema

┌─────────────────────────────────────────────────────────┐
│ Tu App React │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Componente│ │ Redux │ │ API Calls │ │
│ │ de UI │ │ Actions │ │ │ │
│ └──────┬─────┘ └──────┬─────┘ └──────┬─────┘ │
│ │ │ │ │
│ └────────────────┴────────────────┘ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Logger Service │ │
│ │ (tu implementación) │ │
│ └────────┬───────────────┘ │
└─────────────────────┼───────────────────────────────────┘
┌─────────────────┐
│ GM Logs API │
│ (puerto 8080) │
└────────┬─────────┘
┌───────────────────────────────┐
│ Azure Table Storage │
│ + Azure Blob Storage │
└───────────────────────────────┘

Flujo de un Log

  1. Tu app genera un evento (error, acción del usuario, etc.)
  2. Llamas al servicio de logging con los datos relevantes
  3. El servicio construye el payload JSON según el modelo
  4. Se envía POST a la API de GM Logs con el API Key
  5. GM Logs almacena y hace disponible en el dashboard

Niveles de Log y Cuándo Usarlos

GM Logs soporta 5 niveles de severidad. Usar el nivel correcto es crucial para filtrar y priorizar problemas.

Tabla de Niveles

NivelCuándo UsarEjemplosColor en DashboardRequiere Acción
DEBUGInformación detallada para debugging durante desarrollo”Estado del Redux actualizado”, “API response received”, “Form validation passed”AzulNo
INFOEventos normales del sistema que son importantes pero no problemáticos”Usuario inició sesión”, “Pedido creado exitosamente”, “Navegó a /checkout”VerdeNo
WARNINGSituaciones anormales que NO impiden el funcionamiento pero merecen atención”API tardó 5 segundos”, “Usuario intentó acceso sin permisos”, “Rate limit cercano”AmarilloRevisar después
ERRORErrores que impiden una operación específica pero no crashean la app”Falló al cargar pedidos”, “Error al procesar pago”, “Network timeout”NaranjaSi
FATALErrores críticos que crashean o dejan la app inutilizable”App crash”, “No se puede conectar a ningún servicio”, “State corrupto irrecuperable”RojoUrgente

Guía de Decisión Rápida

¿El usuario puede continuar usando la app?
├─ Sí
│ ├─ ¿Es información de desarrollo?
│ │ └─ DEBUG
│ └─ ¿Es comportamiento esperado?
│ ├─ Sí → INFO
│ └─ No → WARNING
└─ No
├─ ¿Solo afecta una función específica?
│ └─ ERROR
└─ ¿La app entera está rota?
└─ FATAL

Campos del Modelo de Log

Campos Obligatorios

Estos campos SIEMPRE deben enviarse:

CampoTipoDescripciónEjemplo
rfcstringRFC del cliente/empresa (identificador fiscal mexicano)"ABC123456XYZ"
userIdstringID único del usuario que generó el log"user_123", "anonymous"
levelstringNivel de severidad"ERROR", "INFO"
messagestringDescripción clara del evento (max 5000 chars)"Error al procesar pago con tarjeta"
sourcestringComponente/módulo que generó el log"PaymentForm", "AuthService"
applicationstringIdentificador de tu aplicación"gm-mis-viajes-mobile", "gm-erp-web"

Campos Opcionales pero Recomendados

CampoTipoDescripciónEjemplo¿Cuándo usarlo?
ticketIdstringID del ticket de soporte asociado"TICKET-2025-001"Cuando el log está relacionado con un issue reportado
environmentstringAmbiente de ejecución"production", "staging", "development"Siempre, ayuda a filtrar
correlationIdstringID para rastrear una operación completa"uuid-v4"Para flujos multi-paso (checkout, registro, etc.)
sessionIdstringID de la sesión del usuario"session_abc123"Para rastrear toda la actividad de un usuario
tagsstring[]Etiquetas para categorización["payment", "stripe", "credit-card"]Para búsquedas y filtros en dashboard
metadataobjectDatos adicionales en formato JSON{"amount": 150, "currency": "MXN"}Contexto específico del evento
versionstringVersión de tu app"2.3.1"Para identificar qué versión generó el error
ipAddressstringIP del cliente"192.168.1.100"Para análisis de seguridad o geolocalización

Campos de Adjuntos (Opcionales)

Úsalos cuando necesites enviar archivos completos (stack traces largos, respuestas de API, etc.):

CampoTipoDescripción
attachmentContentstringContenido del archivo en texto (< 4KB se guarda inline, >= 4KB en blob)
attachmentFileNamestringNombre del archivo (ej: "stacktrace.log")

Endpoints Disponibles (POST)

El sistema tiene 2 endpoints principales para enviar logs:

1. POST /api/v1/logs - Log Simple (JSON)

¿Cuándo usarlo?

  • Logs normales sin archivos adjuntos
  • Mensajes de hasta 5000 caracteres
  • 99% de los casos de uso

URL Completa:

http://your-gm-logs-server:8080/api/v1/logs

Headers Requeridos:

Content-Type: application/json
X-API-Key: tu-api-key-aqui

Body (JSON):

{
"rfc": "ABC123456XYZ",
"userId": "user_123",
"level": "ERROR",
"message": "Error al procesar pago con tarjeta Visa",
"source": "PaymentForm",
"application": "gm-mis-viajes-mobile",
"ticketId": "TICKET-2025-042",
"environment": "production",
"correlationId": "550e8400-e29b-41d4-a716-446655440000",
"sessionId": "session_abc123xyz",
"tags": ["payment", "stripe", "error"],
"metadata": {
"amount": 1500.5,
"currency": "MXN",
"cardBrand": "Visa",
"errorCode": "card_declined",
"lastFourDigits": "4242"
},
"version": "2.3.1",
"ipAddress": "192.168.1.100"
}

Respuesta Exitosa (201 Created):

{
"success": true,
"message": "Log created successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"rfc": "ABC123456XYZ",
"level": "ERROR",
"message": "Error al procesar pago con tarjeta Visa",
"timestamp": "2025-10-07T14:30:00Z",
"createdAt": "2025-10-07T14:30:00Z"
}
}

2. POST /api/v1/logs/upload - Log con Archivo (Multipart)

¿Cuándo usarlo?

  • Stack traces muy largos (> 5000 caracteres)
  • Respuestas completas de APIs externas
  • Archivos de configuración que causaron errores
  • Dumps de estado de Redux/Vuex

URL Completa:

http://your-gm-logs-server:8080/api/v1/logs/upload

Headers Requeridos:

Content-Type: multipart/form-data
X-API-Key: tu-api-key-aqui

Form Data (Multipart):

file: [archivo binario .log, .txt, .json, .xml, .csv]
rfc: "ABC123456XYZ"
userId: "user_123"
level: "ERROR"
message: "Stack trace completo del crash en producción"
source: "App.tsx"
application: "gm-mis-viajes-mobile"
ticketId: "TICKET-2025-042"
environment: "production"
correlationId: "550e8400-e29b-41d4-a716-446655440000"
sessionId: "session_abc123xyz"
tags: "crash,fatal,production"
metadata: '{"osVersion":"iOS 17.0","deviceModel":"iPhone 14 Pro"}'
version: "2.3.1"
ipAddress: "192.168.1.100"

Límites del Archivo:

  • Tamaño máximo: 10 MB
  • Tipos permitidos: .log, .txt, .json, .xml, .csv
  • Estrategia de almacenamiento:
    • Archivos < 4 KB: Se guardan inline en Azure Table Storage
    • Archivos ≥ 4 KB: Se guardan en Azure Blob Storage (más eficiente)

Respuesta Exitosa (201 Created):

{
"success": true,
"message": "Log with file uploaded successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"fileName": "stacktrace.log",
"fileSize": 15234,
"storageType": "blob",
"downloadUrl": "http://your-server/api/v1/logs/ABC123456XYZ/550e8400/attachment"
}
}

Mejores Prácticas

1. Construcción de Mensajes Claros

Mal:

{
"message": "Error"
}

Bien:

{
"message": "Error al procesar pago con tarjeta Visa terminada en 4242: card_declined",
"metadata": {
"errorCode": "card_declined",
"paymentMethod": "visa",
"lastFour": "4242",
"attemptNumber": 2
}
}

Reglas para mensajes:

  • Sé específico: ¿Qué operación falló?
  • Incluye contexto: ¿Qué estaba haciendo el usuario?
  • Si hay un error code, inclúyelo en el mensaje
  • Usa lenguaje natural, no solo códigos técnicos

2. Uso Efectivo de Tags

Los tags son tu mejor amigo para filtrar en el dashboard.

Estrategia recomendada: 3-5 tags por log

  • 1 tag de categoría (payment, auth, navigation, etc.)
  • 1 tag de tecnología (stripe, firebase, axios, etc.)
  • 1-2 tags de contexto (checkout, profile, dashboard, etc.)
  • 1 tag de severidad extra si aplica (critical, security, etc.)

Ejemplos:

// Pago fallido
"tags": ["payment", "stripe", "checkout", "credit-card", "declined"]
// Error de autenticación
"tags": ["auth", "firebase", "login", "invalid-credentials"]
// Navegación con warning
"tags": ["navigation", "slow-load", "performance", "network"]
// Crash de la app
"tags": ["crash", "fatal", "memory-leak", "production"]

3. Metadata Estructurada

El campo metadata es un objeto JSON flexible. Úsalo para datos estructurados.

Buenas prácticas:

{
"metadata": {
"payment": {
"amount": 1500.5,
"currency": "MXN",
"method": "credit_card",
"provider": "stripe"
},
"user": {
"plan": "premium",
"registeredDays": 45
},
"request": {
"endpoint": "/api/v1/payments",
"method": "POST",
"statusCode": 402
},
"device": {
"os": "iOS",
"version": "17.0",
"model": "iPhone 14 Pro"
}
}
}

Evita:

  • Guardar tokens o contraseñas
  • Información sensible de tarjetas (solo últimos 4 dígitos está ok)
  • Datos personales innecesarios (emails completos, teléfonos)
  • Objetos circulares (causarán error al serializar)

4. Estrategia de Environments

Usa el campo environment consistentemente:

const environment =
process.env.NODE_ENV === "production"
? "production"
: process.env.NODE_ENV === "staging"
? "staging"
: "development";

¿Por qué es importante?

  • Filtrar logs de producción vs desarrollo en el dashboard
  • Evitar contaminar métricas con logs de testing
  • Priorizar errores de producción

5. Rate Limiting y Batching

Para evitar sobrecargar el sistema con miles de logs:

Implementa debouncing:

// Pseudocódigo
const debouncedLogger = debounce((logData) => {
sendToGMLogs(logData);
}, 1000);
// En lugar de loggear en cada render:
useEffect(() => {
debouncedLogger({ message: "Component rendered" });
}, [dependency]);

Agrupa logs similares:

// Si un error se repite, no envíes 100 logs iguales
const errorCount = {};
function logError(error) {
const key = error.message;
errorCount[key] = (errorCount[key] || 0) + 1;
// Solo envía cada 10 ocurrencias
if (errorCount[key] % 10 === 1) {
sendToGMLogs({
level: "ERROR",
message: error.message,
metadata: { occurrences: errorCount[key] },
});
}
}

6. No Loguees TODO

No hagas esto:

// Cada vez que un componente se monta
useEffect(() => {
logger.debug("Component mounted");
}, []);
// Cada click del mouse
onClick={() => {
logger.info("User clicked button");
}}

Loguea eventos significativos:

  • Errores y excepciones
  • Operaciones críticas (pagos, autenticación, cambios de estado importantes)
  • Eventos de negocio relevantes
  • Warnings de performance (> 3 segundos)

Casos de Uso Prácticos

Caso 1: Error al Procesar Pago

Escenario: Usuario intenta pagar pero la tarjeta es rechazada por el proveedor (Stripe).

Log a enviar:

{
"rfc": "GMT950101ABC",
"userId": "user_12345",
"ticketId": null,
"level": "ERROR",
"message": "Payment declined by Stripe: card_declined - Insufficient funds",
"source": "PaymentForm",
"application": "gm-mis-viajes-mobile",
"environment": "production",
"correlationId": "pay_550e8400e29b41d4a716",
"sessionId": "session_abc123",
"tags": ["payment", "stripe", "declined", "checkout"],
"metadata": {
"amount": 1850.0,
"currency": "MXN",
"paymentMethod": "card",
"cardBrand": "Visa",
"cardLast4": "4242",
"stripeErrorCode": "card_declined",
"stripeErrorMessage": "Your card has insufficient funds",
"attemptNumber": 1,
"orderId": "ORD-2025-10-07-12345"
},
"version": "3.2.1",
"ipAddress": "201.141.23.45"
}

¿Por qué este log es bueno?

  • Nivel ERROR correcto (operación falló)
  • Mensaje descriptivo con código de error
  • Tags permiten filtrar todos los errores de pago
  • Metadata tiene contexto completo para debugging
  • CorrelationID permite rastrear todo el flujo de checkout

Caso 2: Usuario Inició Sesión Exitosamente

Escenario: Usuario se autentica correctamente en la app.

Log a enviar:

{
"rfc": "GMT950101ABC",
"userId": "user_67890",
"level": "INFO",
"message": "User logged in successfully via Google OAuth",
"source": "AuthService",
"application": "gm-erp-web",
"environment": "production",
"sessionId": "session_new_xyz789",
"tags": ["auth", "login", "oauth", "google"],
"metadata": {
"authProvider": "google",
"isFirstLogin": false,
"lastLoginDaysAgo": 3,
"userPlan": "premium"
},
"version": "1.5.2",
"ipAddress": "189.203.45.67"
}

¿Por qué INFO y no DEBUG?

  • Es un evento de negocio importante
  • Útil para auditoría de seguridad
  • Permite analizar patrones de login

Caso 3: API Externa Lenta (Warning)

Escenario: La API de un proveedor externo está tardando más de lo normal.

Log a enviar:

{
"rfc": "GMT950101ABC",
"userId": "user_11111",
"level": "WARNING",
"message": "External API response time exceeded threshold: 8.5 seconds",
"source": "PaymentGatewayClient",
"application": "gm-mis-viajes-backend-proxy",
"environment": "production",
"correlationId": "req_550e8400e29b41d4a716",
"tags": ["performance", "api", "stripe", "slow-response", "sla-breach"],
"metadata": {
"endpoint": "https://api.stripe.com/v1/payment_intents",
"method": "POST",
"responseTime": 8500,
"thresholdMs": 3000,
"statusCode": 200,
"retryCount": 0
},
"version": "2.1.0"
}

¿Por qué WARNING?

  • La operación funcionó, pero no óptimamente
  • Indica un problema potencial que puede escalar
  • Permite identificar degradación de servicios antes de que fallen

Caso 4: Crash de la App (Fatal)

Escenario: La app de React crashea por un error no capturado.

Log a enviar (con archivo):

Usa el endpoint /api/v1/logs/upload con multipart/form-data:

Form Data:

file: [archivo stacktrace.log con el stack trace completo]
rfc: "GMT950101ABC"
userId: "user_22222"
level: "FATAL"
message: "Unhandled exception crashed the app: TypeError: Cannot read property 'map' of undefined"
source: "ErrorBoundary"
application: "gm-mis-viajes-mobile"
environment: "production"
sessionId: "session_crash_abc"
tags: "crash,fatal,unhandled-exception,production"
metadata: '{"componentStack":"at OrderList","errorBoundary":"AppErrorBoundary","osVersion":"iOS 17.0","deviceModel":"iPhone 14 Pro","appState":"foreground"}'
version: "2.3.1"
ipAddress: "192.168.1.50"

Contenido del archivo stacktrace.log:

Error: Cannot read property 'map' of undefined
at OrderList (OrderList.tsx:42)
at renderWithHooks (react-dom.js:1234)
at mountIndeterminateComponent (react-dom.js:5678)
at beginWork (react-dom.js:9012)
...
[Stack trace completo de 500 líneas]

¿Por qué FATAL?

  • La app quedó inutilizable
  • Requiere acción inmediata
  • Se adjunta stack trace completo para debugging

Caso 5: Flujo Multi-Paso con Correlation ID

Escenario: Usuario realiza un checkout de 3 pasos. Queremos rastrear todo el flujo.

Paso 1: Inicio del Checkout

{
"rfc": "GMT950101ABC",
"userId": "user_33333",
"level": "INFO",
"message": "Checkout flow started",
"source": "CheckoutController",
"application": "gm-erp-web",
"environment": "production",
"correlationId": "checkout_550e8400e29b41d4a716",
"sessionId": "session_xyz",
"tags": ["checkout", "flow-start", "cart"],
"metadata": {
"step": "1-cart-review",
"itemCount": 3,
"totalAmount": 2450.0
}
}

Paso 2: Dirección de Envío Completada

{
"rfc": "GMT950101ABC",
"userId": "user_33333",
"level": "INFO",
"message": "Shipping address validated",
"source": "CheckoutController",
"application": "gm-erp-web",
"environment": "production",
"correlationId": "checkout_550e8400e29b41d4a716",
"sessionId": "session_xyz",
"tags": ["checkout", "shipping", "address"],
"metadata": {
"step": "2-shipping-address",
"state": "Ciudad de México",
"postalCode": "03100"
}
}

Paso 3: Pago Completado

{
"rfc": "GMT950101ABC",
"userId": "user_33333",
"level": "INFO",
"message": "Checkout flow completed successfully",
"source": "CheckoutController",
"application": "gm-erp-web",
"environment": "production",
"correlationId": "checkout_550e8400e29b41d4a716",
"sessionId": "session_xyz",
"tags": ["checkout", "flow-complete", "payment", "success"],
"metadata": {
"step": "3-payment-complete",
"orderId": "ORD-2025-10-07-99999",
"paymentMethod": "credit_card",
"totalAmount": 2450.0,
"totalDurationSeconds": 145
}
}

Beneficio del Correlation ID: En el dashboard, puedes filtrar por correlationId: "checkout_550e8400e29b41d4a716" y ver TODOS los logs de este checkout específico, incluso si pasó por múltiples servicios.


Sistema de Tickets

¿Qué es un Ticket ID?

El ticketId es un campo crucial que vincula logs con issues reportados por usuarios o detectados por el equipo de soporte.

Formato recomendado:

TICKET-YYYY-NNN
Ejemplo: TICKET-2025-042

Flujo de Trabajo con Tickets

1. Usuario reporta problema
└─> Soporte crea ticket: "TICKET-2025-042"
2. Soporte pide al usuario reproducir el problema
└─> Usuario usa la app
└─> App envía logs con ticketId: "TICKET-2025-042"
3. Desarrollador filtra en dashboard por ticketId
└─> Ve TODOS los logs relacionados con ese problema
└─> Puede debuggear con contexto completo

Implementación en Frontend

Opción 1: Manual (URL o Input)

// El usuario ingresa el ticket ID en settings
const ticketId = localStorage.getItem("activeTicketId") || null;
logger.error({
ticketId,
message: "Error reproducido con ticket activo",
});

Opción 2: Parámetro de Query String

// URL: https://tu-app.com?ticket=TICKET-2025-042
const params = new URLSearchParams(window.location.search);
const ticketId = params.get("ticket");
logger.error({
ticketId,
message: "Error capturado con ticket en URL",
});

Opción 3: Configuración Global

// En tu servicio de logging
class LoggerService {
constructor() {
this.activeTicketId = null;
}
setActiveTicket(ticketId) {
this.activeTicketId = ticketId;
}
clearActiveTicket() {
this.activeTicketId = null;
}
async log(logData) {
// Agregar ticket automáticamente si está activo
const payload = {
...logData,
ticketId: this.activeTicketId || logData.ticketId,
};
await sendToAPI(payload);
}
}

Consulta en Dashboard

Una vez enviados los logs con ticketId, el equipo puede:

  1. Ir al dashboard
  2. Filtrar por Ticket ID: TICKET-2025-042
  3. Ver cronológicamente TODO lo que pasó
  4. Analizar el contexto completo del error

Correlation ID y Trazabilidad

¿Qué es un Correlation ID?

Es un identificador único (UUID) que vinculas a todos los logs de una operación completa que puede abarcar múltiples acciones.

Ejemplos de operaciones:

  • Proceso de checkout (3-5 pasos)
  • Registro de usuario (formulario → email → verificación)
  • Carga de datos de un dashboard (múltiples API calls)
  • Flujo de autenticación (login → 2FA → redirect)

¿Por qué es Importante?

Sin correlation ID:

[LOG 1] "User clicked checkout button"
[LOG 2] "Payment API called"
[LOG 3] "Order created"
[LOG 4] "Email sent"

Problema: ¿Estos 4 logs son del mismo checkout? ¿O de 4 usuarios diferentes?

Con correlation ID:

[LOG 1] correlationId: "abc-123" "User clicked checkout button"
[LOG 2] correlationId: "abc-123" "Payment API called"
[LOG 3] correlationId: "abc-123" "Order created"
[LOG 4] correlationId: "abc-123" "Email sent"

Solución: Todos tienen el mismo correlationId = mismo flujo.

Implementación

Generar un Correlation ID:

import { v4 as uuidv4 } from "uuid";
// Al iniciar una operación importante
const correlationId = uuidv4();
// Ejemplo: "550e8400-e29b-41d4-a716-446655440000"

Propagar el Correlation ID:

Opción 1: Context API de React

// Contexto global
const CorrelationContext = React.createContext();
function CheckoutFlow() {
const [correlationId] = useState(() => uuidv4());
return (
<CorrelationContext.Provider value={correlationId}>
<CartStep />
<ShippingStep />
<PaymentStep />
</CorrelationContext.Provider>
);
}
// En cualquier componente hijo
function PaymentStep() {
const correlationId = useContext(CorrelationContext);
const handlePayment = async () => {
logger.info({
correlationId,
message: "Processing payment",
});
};
}

Opción 2: Redux State

// Action
const startCheckout = () => ({
type: "CHECKOUT_START",
payload: { correlationId: uuidv4() },
});
// En cualquier parte con acceso a Redux
const correlationId = useSelector((state) => state.checkout.correlationId);
logger.info({
correlationId,
message: "Step completed",
});

Mejores Prácticas de Correlation

  1. Un Correlation ID por operación de negocio

    • Checkout: 1 ID para todo el flujo
    • Login: 1 ID desde inicio hasta redirect
  2. Incluir en todas las llamadas de API

    fetch("/api/payment", {
    headers: {
    "X-Correlation-ID": correlationId,
    },
    });
  3. Propagar a logs de backend

    • Si tu backend también usa GM Logs, pasa el mismo correlationId
    • Permite rastreo end-to-end (frontend → backend → DB)
  4. No reutilizar IDs

    • Cada nueva operación = nuevo correlationId
    • Si el usuario reinicia el checkout, genera uno nuevo

Manejo de Errores

Errores de la API de Logs

La API puede devolver estos errores:

StatusErrorCausaSolución
401UnauthorizedAPI Key inválido o faltanteVerifica el header X-API-Key
400Bad RequestCampos inválidos o faltantesRevisa el modelo y campos obligatorios
413Payload Too LargeArchivo > 10 MBDivide el archivo o usa compresión
500Internal Server ErrorError en el servidorReintenta con backoff exponencial

Estrategia de Retry

Implementa reintentos automáticos:

async function sendLogWithRetry(logData, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch("http://server/api/v1/logs", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
body: JSON.stringify(logData),
});
if (response.ok) return await response.json();
// Si es 4xx (error del cliente), no reintentar
if (response.status >= 400 && response.status < 500) {
console.error("Client error, no retry:", response.status);
return null;
}
// Si es 5xx (error del servidor), reintentar
if (i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Backoff exponencial
await new Promise((resolve) => setTimeout(resolve, delay));
}
} catch (error) {
console.error("Network error:", error);
if (i === maxRetries - 1) return null;
}
}
return null;
}

Logging Offline

Si tu app funciona offline, considera un buffer local:

class OfflineLogBuffer {
constructor() {
this.buffer = JSON.parse(localStorage.getItem("logBuffer")) || [];
}
add(logData) {
this.buffer.push(logData);
localStorage.setItem("logBuffer", JSON.stringify(this.buffer));
}
async flush() {
if (this.buffer.length === 0) return;
const logs = [...this.buffer];
this.buffer = [];
localStorage.setItem("logBuffer", JSON.stringify(this.buffer));
for (const log of logs) {
await sendLogWithRetry(log);
}
}
}
// Uso
const buffer = new OfflineLogBuffer();
// Al intentar enviar un log
try {
await sendLogWithRetry(logData);
} catch (error) {
// Si falla, guardar en buffer
buffer.add(logData);
}
// Cuando vuelve la conexión
window.addEventListener("online", () => {
buffer.flush();
});

Ejemplos Completos

Ejemplo 1: Logger Service Básico

loggerService.js
import { v4 as uuidv4 } from "uuid";
const API_BASE_URL = "http://your-server:8080/api/v1";
const API_KEY = process.env.REACT_APP_GM_LOGS_API_KEY;
const APPLICATION = "gm-mis-viajes-mobile";
class LoggerService {
constructor() {
this.rfc = null;
this.userId = null;
this.sessionId = uuidv4();
this.environment =
process.env.NODE_ENV === "production" ? "production" : "development";
this.version = process.env.REACT_APP_VERSION || "0.0.0";
}
setUser(rfc, userId) {
this.rfc = rfc;
this.userId = userId;
}
async log(level, message, options = {}) {
if (!this.rfc || !this.userId) {
console.warn("Logger: RFC and UserID not set");
return;
}
const payload = {
rfc: this.rfc,
userId: this.userId,
level,
message,
source: options.source || "UnknownComponent",
application: APPLICATION,
sessionId: this.sessionId,
environment: this.environment,
version: this.version,
timestamp: new Date().toISOString(),
...options,
};
try {
const response = await fetch(`${API_BASE_URL}/logs`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
body: JSON.stringify(payload),
});
if (!response.ok) {
console.error("Failed to send log:", response.status);
}
} catch (error) {
console.error("Logger error:", error);
}
}
debug(message, options) {
return this.log("DEBUG", message, options);
}
info(message, options) {
return this.log("INFO", message, options);
}
warning(message, options) {
return this.log("WARNING", message, options);
}
error(message, options) {
return this.log("ERROR", message, options);
}
fatal(message, options) {
return this.log("FATAL", message, options);
}
}
export default new LoggerService();

Uso:

import logger from "./loggerService";
// Configurar al inicio de la app
logger.setUser("ABC123456XYZ", "user_12345");
// Loguear eventos
logger.info("User logged in", {
source: "LoginForm",
tags: ["auth", "login"],
metadata: { authProvider: "google" },
});
logger.error("Payment failed", {
source: "PaymentForm",
tags: ["payment", "error"],
metadata: { errorCode: "card_declined" },
});

Ejemplo 2: Error Boundary con Logging

ErrorBoundary.jsx
import React from "react";
import logger from "./loggerService";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Enviar log FATAL
logger.fatal(`React Error Boundary caught error: ${error.message}`, {
source: "ErrorBoundary",
tags: ["crash", "react", "unhandled-exception"],
metadata: {
error: error.toString(),
componentStack: errorInfo.componentStack,
errorName: error.name,
errorStack: error.stack,
},
attachmentContent: error.stack,
attachmentFileName: "error-stack.log",
});
}
render() {
if (this.state.hasError) {
return (
<div>
<h1>Algo salió mal</h1>
<p>Nuestro equipo ha sido notificado del problema.</p>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;

Ejemplo 3: Axios Interceptor para Loguear Errores de API

axiosConfig.js
import axios from "axios";
import logger from "./loggerService";
const api = axios.create({
baseURL: "https://your-api.com",
});
// Interceptor de respuestas
api.interceptors.response.use(
(response) => response,
(error) => {
const { config, response } = error;
// Loguear error de API
logger.error(
`API Error: ${config.method.toUpperCase()} ${config.url} failed`,
{
source: "AxiosInterceptor",
tags: ["api", "http-error", `status-${response?.status}`],
metadata: {
method: config.method,
url: config.url,
statusCode: response?.status,
statusText: response?.statusText,
responseData: response?.data,
requestData: config.data,
},
}
);
return Promise.reject(error);
}
);
export default api;

Ejemplo 4: Tracking de Performance

performanceTracker.js
import logger from "./loggerService";
export function trackPerformance(operationName, threshold = 3000) {
const startTime = performance.now();
return {
finish: () => {
const duration = performance.now() - startTime;
if (duration > threshold) {
logger.warning(
`Slow operation: ${operationName} took ${duration.toFixed(0)}ms`,
{
source: "PerformanceTracker",
tags: ["performance", "slow-operation"],
metadata: {
operation: operationName,
durationMs: Math.round(duration),
thresholdMs: threshold,
},
}
);
}
},
};
}
// Uso
const tracker = trackPerformance("LoadDashboardData");
await fetchDashboardData();
tracker.finish();

Seguridad y API Keys

Obtención del API Key

  1. Contacta al equipo de DevOps/Backend de GM Transport
  2. Solicita un API Key para tu aplicación
  3. Especifica el ambiente (development, staging, production)
  4. Te proporcionarán un key con el formato: gm-logs-{env}-{random}

Almacenamiento Seguro

Nunca hagas esto:

// Hardcodear el key en el código
const API_KEY = "gm-logs-prod-abc123xyz";

Usa variables de entorno:

.env.production

REACT_APP_GM_LOGS_API_KEY=gm-logs-prod-abc123xyz
REACT_APP_GM_LOGS_BASE_URL=https://logs.gmtransport.com

Código:

const API_KEY = process.env.REACT_APP_GM_LOGS_API_KEY;
const BASE_URL = process.env.REACT_APP_GM_LOGS_BASE_URL;

Rate Limits

Para evitar abuse, el sistema tiene límites:

  • Max requests: 1000 logs por minuto por API Key
  • Max file size: 10 MB por archivo
  • Max message length: 5000 caracteres

Si superas estos límites, recibirás un 429 Too Many Requests.


Checklist Final

Antes de considerar tu integración completa, verifica:

  • Tienes un servicio centralizado de logging
  • Configuraste el API Key en variables de entorno
  • Implementaste los 5 niveles de log correctamente
  • Usas tags consistentes y descriptivos
  • El campo application identifica tu app
  • El campo environment refleja el ambiente correcto
  • Implementaste Correlation ID para flujos importantes
  • Configuraste Error Boundaries para crasheos
  • Implementaste retry logic para fallos de red
  • No logueas información sensible (passwords, tokens completos)
  • Probaste el envío de logs en desarrollo
  • Validaste que los logs aparecen en el dashboard

Recursos Adicionales

  • Swagger UI: http://your-server:8080/swagger/index.html
  • Dashboard: http://your-server:8080/api/v1/stats/dashboard
  • Postman Collection: Disponible en docs/GM_Logs_API_Complete_FULL_RESTful.postman_collection.json
  • Soporte: Contacta al equipo de Backend de GM Transport

🤝 Soporte

Si tienes dudas o encuentras problemas:

  1. Revisa esta guía completa
  2. Consulta el Swagger UI para detalles técnicos
  3. Contacta al equipo de Backend:
    • Email: [email protected]
    • Slack: #gm-logs-support

Confidencial: Este documento es para uso interno de GM Transport. No distribuir externamente.


Versión: 2.0
Última actualización: Octubre 2025
Autor: Equipo de Proyectos Nuevos - GM Transport