feat: filtro de severidad (Alta/Media/Baja) en el log de errores
Se asigna sola, sin etiquetar nada a mano: backend y errores de render de React (rompen toda la pantalla) = alta; errores JS sueltos = media; promesas rechazadas sin manejar = baja (suelen ser fallos de red puntuales, no criticos). Nuevo filtro en el panel ademas del de origen ya existente. Migracion 014_error_logs_severity.sql (ALTER TABLE ADD COLUMN IF NOT EXISTS). Probado en dev: las tres severidades se asignan correctamente segun el origen, un valor invalido del frontend cae en media, y el filtro combinado con origen funciona. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
-- Severidad automatica (nunca etiquetada a mano): backend y errores de render de React (rompen
|
||||
-- toda la pantalla) = alta; errores JS sueltos = media; promesas rechazadas sin catch = baja.
|
||||
-- Filas ya existentes (si las hay) quedan en 'media' por defecto, criterio neutral razonable.
|
||||
ALTER TABLE error_logs ADD COLUMN IF NOT EXISTS severity ENUM('alta', 'media', 'baja') NOT NULL DEFAULT 'media' AFTER source;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "project-linkedcorp-backend",
|
||||
"version": "1.21.0",
|
||||
"version": "1.21.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/server.js",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ok, asyncHandler } from '../../utils/http.js';
|
||||
import * as service from './service.js';
|
||||
|
||||
export const getErrorLogs = asyncHandler(async (req, res) => {
|
||||
ok(res, await service.listErrors({ source: req.query.source, limit: req.query.limit }));
|
||||
ok(res, await service.listErrors({ source: req.query.source, severity: req.query.severity, limit: req.query.limit }));
|
||||
});
|
||||
|
||||
export const postErrorLog = asyncHandler(async (req, res) => {
|
||||
|
||||
@@ -4,11 +4,11 @@ import { pool } from '../../config/db.js';
|
||||
// errores): al insertar, se recorta a los MAX_ROWS mas recientes.
|
||||
const MAX_ROWS = 1000;
|
||||
|
||||
export async function create({ source, message, stack, url, method, userId, userAgent }) {
|
||||
export async function create({ source, severity, message, stack, url, method, userId, userAgent }) {
|
||||
await pool.query(
|
||||
`INSERT INTO error_logs (source, message, stack, url, method, user_id, user_agent)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
[source, message, stack || null, url || null, method || null, userId || null, userAgent || null]
|
||||
`INSERT INTO error_logs (source, severity, message, stack, url, method, user_id, user_agent)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[source, severity, message, stack || null, url || null, method || null, userId || null, userAgent || null]
|
||||
);
|
||||
await pool.query(
|
||||
`DELETE FROM error_logs WHERE id NOT IN (SELECT id FROM (SELECT id FROM error_logs ORDER BY created_at DESC LIMIT ?) x)`,
|
||||
@@ -16,13 +16,14 @@ export async function create({ source, message, stack, url, method, userId, user
|
||||
);
|
||||
}
|
||||
|
||||
export async function findAll({ source, limit = 200 } = {}) {
|
||||
export async function findAll({ source, severity, limit = 200 } = {}) {
|
||||
const clauses = [];
|
||||
const params = [];
|
||||
if (source) { clauses.push('el.source = ?'); params.push(source); }
|
||||
if (severity) { clauses.push('el.severity = ?'); params.push(severity); }
|
||||
const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
|
||||
const [rows] = await pool.query(
|
||||
`SELECT el.id, el.source, el.message, el.stack, el.url, el.method,
|
||||
`SELECT el.id, el.source, el.severity, el.message, el.stack, el.url, el.method,
|
||||
el.user_id, u.name AS user_name, el.user_agent, el.created_at
|
||||
FROM error_logs el LEFT JOIN users u ON u.id = el.user_id
|
||||
${where} ORDER BY el.created_at DESC LIMIT ?`,
|
||||
|
||||
@@ -3,12 +3,23 @@ import * as repo from './repository.js';
|
||||
import { env } from '../../config/env.js';
|
||||
import { HttpError } from '../../utils/http.js';
|
||||
|
||||
// Severidad SIEMPRE automatica, nunca etiquetada a mano (mismo criterio que la criticidad del
|
||||
// panel de Actualizaciones): backend y errores de render de React "alta" (rompen toda la
|
||||
// pantalla), errores JS sueltos "media", promesas rechazadas sin catch "baja" (suelen ser fallos
|
||||
// de red no criticos). El frontend indica cual de los tres le paso; si manda algo invalido u
|
||||
// omite el dato, cae en "media" como default neutral.
|
||||
const VALID_SEVERITIES = new Set(['alta', 'media', 'baja']);
|
||||
function normalizeSeverity(value) {
|
||||
return VALID_SEVERITIES.has(value) ? value : 'media';
|
||||
}
|
||||
|
||||
// Nunca debe tirar: si falla el logueo de un error, no puede convertirse en OTRO error sin
|
||||
// manejar que rompa la respuesta original. Se traga cualquier fallo propio (solo avisa por consola).
|
||||
export async function logBackendError(err, req) {
|
||||
try {
|
||||
await repo.create({
|
||||
source: 'backend',
|
||||
severity: 'alta',
|
||||
message: (err?.message || String(err)).slice(0, 2000),
|
||||
stack: (err?.stack || '').slice(0, 5000) || null,
|
||||
url: req?.originalUrl?.slice(0, 500) || null,
|
||||
@@ -39,6 +50,7 @@ export async function logFrontendError(payload, req) {
|
||||
if (!message) throw new HttpError(400, 'message es obligatorio');
|
||||
await repo.create({
|
||||
source: 'frontend',
|
||||
severity: normalizeSeverity(payload.severity),
|
||||
message,
|
||||
stack: (payload.stack || '').slice(0, 5000) || null,
|
||||
url: (payload.url || '').slice(0, 500) || null,
|
||||
|
||||
Reference in New Issue
Block a user