Skip to content

Registro de errores

Audience

Este artículo es para operadores de la plataforma GCM. Los administradores de iglesia deberían ver el artículo del registro de auditoría en Usuarios y roles para cambios que pueden investigar ellos mismos — el registro de errores es a nivel de plataforma e incluye trazas de pila y cuerpos de petición, que son exclusivos del personal de GCM por diseño.

La pestaña Registro de errores (/platform/error-log) es la cola central de triaje para todo lo que explota en cualquier parte del sistema. Excepciones de frontend capturadas por el límite de Sentry, edge functions que lanzan, rutas de API de Vercel que devuelven 500 y triggers de base de datos que disparan — todos convergen en platform_error_log y emergen aquí, filtrables por fuente, severidad, organización y estado resuelto, auto-actualizándose cada 30 segundos.

Registro de errores con filtros y panel de detalle

Las cinco fuentes

Cada fila lleva un enum source. El desplegable de filtro te permite acotar a uno a la vez:

  • Frontend — excepciones de React no capturadas. Capturadas por el límite de error global y publicadas vía /api/log-error. Los campos url y user_agent están rellenados.
  • Edge function — edge functions de Deno que lanzaron o llamaron a log() con severidad error. La columna function_name está establecida.
  • API de Vercel — funciones serverless de Node (las rutas /api/*). Como las edge functions pero con el contexto runtime de Vercel.
  • Trigger de DB — disparado desde dentro de Postgres, normalmente por un RAISE EXCEPTION en un trigger. La traza de pila es el contexto de error de Postgres.
  • Manual — cualquier cosa para la que log_error fue llamado explícitamente. Usado para advertencias no fatales que merece la pena emerger.

Severidad

Tres niveles, todos codificados por color:

  • warning — ámbar. No fallan las peticiones, pero suficientemente ruidosos para investigar. Denegaciones de RLS, consultas lentas, llamadas reintentadas al gateway.
  • error — rosado. Una petición falló. El usuario vio algo roto.
  • fatal — rojo profundo. Una petición falló de una forma que dejó el estado inconsistente. Trátalo como prioridad uno.

El filtro por defecto es "no resuelto"

La página carga con resolved = false para que la cola sea lo que está abierto. Cambia a Resueltos para ver historial triado, o Todos para una vista completa. El filtro Org tiene una opción especial Sin atribuir (sin organización) para filas que se dispararon antes de que se estableciera un contexto de inquilino (el caso más común para errores de arranque del frontend).

La búsqueda de texto golpea cuatro columnas a la vez: message, function_name, url, error_code. Úsala como usarías la búsqueda de Sentry — coincidencia parcial, insensible a mayúsculas.

Cómo se ve una fila

Cada fila de la tabla muestra:

  • Cuándo — relativo ("hace 3m"). Pasa el ratón para la marca de tiempo completa.
  • Fuente — ícono coloreado + etiqueta.
  • Severidad — píldora.
  • Org — derivado de context.org_name cuando está presente, si no los primeros 8 caracteres de organization_id, si no un guion.
  • Mensaje — el mensaje de error, truncado.
  • Dóndefunction_name si está disponible, de lo contrario url.
  • Estado — Abierto o Resuelto.

Haz clic en cualquier fila para abrir el diálogo de detalle.

El diálogo de detalle

El diálogo de detalle es el espacio de trabajo de triaje. La mitad superior es una cuadrícula clave/valor: función / URL, código de error, usuario (nombre + correo), org + plan, los roles que tenía, si era administrador de plataforma en el momento, ruta, contexto de acción, id de correlación, user agent.

Siguen dos secciones colapsables:

  • Traza de pila — la pila cruda del runtime. Para triggers de DB este es el contexto de error de Postgres. Para errores de frontend es la pila de JS, con source-map cuando es posible.
  • Contexto (completo) — la columna JSONB context completa, formateada legiblemente. Aquí es donde las edge functions guardan cuerpos de petición, parámetros de consulta y valores intermedios. Trátalo como sensible — puede incluir correos e IDs.

PII en el contexto

La columna context no está redactada. Es un volcado crudo de lo que sea que el código fallido capturó. No la pegues en Slack, en un ticket público ni en un issue de Sentry. El registro de errores vive detrás de la compuerta del administrador de plataforma precisamente para que esto sea seguro.

Resolviendo una fila

La parte inferior del diálogo tiene los controles de resolución:

  • Marcar como resuelto — captura una nota opcional ("arreglado en #123" o "transitorio, ignorando") y estampa resolved, resolved_at, resolved_by, resolved_note. La fila sale de la vista por defecto de no resueltos.
  • Reabrir — solo visible en filas ya resueltas. Limpia las columnas de resolución.
  • Eliminar — destructivo. Elimina la fila individual por completo. Útil para exposiciones de datos personales que no deberían permanecer en la base de datos.

La resolución es una señal de flujo de trabajo, no una solución. Marcar una fila como resuelta no detiene el bug subyacente — solo le dice a tu yo futuro que el error ha sido triado.

Acciones masivas

Dos operaciones masivas viven en la cabecera de la tabla:

Purgar resueltos > 30d

Elimina permanentemente filas resueltas con más de 30 días. Esta es la barrida de retención — una fila triada no merece quedarse más allá de un mes. Barato de ejecutar, seguro de ejecutar regularmente. El botón reporta la cantidad que removió.

Limpiar filtrados

Elimina cada fila que coincida con el conjunto de filtros actual. Esto es peligroso y la interfaz lo sabe:

  • El botón está deshabilitado a menos que al menos un filtro reduzca el conjunto. Sin un filtro, la copia "matching the current filters" del diálogo sería una mentira.
  • El diálogo de confirmación muestra el conteo exacto que eliminará.

Úsalo para limpiar una clase ruidosa de advertencias después de haber lanzado el arreglo: filtra a source = frontend, message = ChunkLoadError, resolved = unresolved, presiona Limpiar, mira el conteo bajar a cero.

Qué significa la auto-actualización

La consulta vuelve a buscar cada 30 segundos vía el refetchInterval de React Query. El spinner de carga aparece en línea junto al conteo de filas en cada refetch — si está girando para siempre, la red está atascada y las filas existentes están obsoletas. El botón Actualizar fuerza una invalidación inmediata.

Patrones comunes

Un pico de advertencias EDGE_FUNCTION de una organización — usualmente un webhook mal configurado golpeando un endpoint que devuelve 429. Abre la organización y verifica sus filas channels.

Errores fatales sin organización — la falla pasó antes de que se resolviera el contexto de inquilino. Crashes de arranque del frontend, errores del auth-hook y fallos de arranque en frío de edge-fn se agrupan aquí. Agrupa por mensaje y la causa suele ser una variable de entorno faltante.

Errores de denegación RLS de db_trigger — alguien está llamando una consulta sin el rol correcto. Cruza el id de usuario en context con el registro de auditoría para ver qué estaban tratando de hacer.

Si una sola fila necesita ser investigada por un ingeniero que no es administrador de plataforma, copia el correlation_id y comparte eso — pueden hacer grep en los logs de edge-function en Supabase con el mismo id sin que tú tengas que reenviar la fila.