Saltar al contenido

Indice / Practicas / EJ-1.4

EJ-1.4 · Log de servidor: sintactico vs semantico

Practica de: 1.5 Las cuatro capas de errorDificultad: A

El archivo JSON es perfectamente válido y sus 800 elementos son cadenas. json.load no mira lo que hay adentro de una cadena: esa segunda etapa del parseo es tuya, y ahí están los nueve problemas.

Qué hay que hacer

A partir de servidor_acceso_log.json (800 líneas de acceso), producir:

Sin pandas: json.load, una expresión regular y un validador por campo.

ELI5Explicado facil

Es la diferencia entre un formulario completo y un formulario verdadero.

Que estén los seis casilleros llenos lo ve cualquiera de un vistazo. Que el casillero «fecha de nacimiento» diga una fecha posible, no: eso hay que leerlo.

Archivos

Las dos clases de línea rota

Cuatro no tienen la forma; cinco la tienen y mienten
LíneaContenidoCapaMotivo
60linea invalida2restriccion_violada
150Le faltan dos campos2restriccion_violada
240Vacía2restriccion_violada
330Método FETCH3dominio_desconocido
420Fecha = fecha_invalida3fecha_invalida
510Estado 9993fuera_de_rango
600Duración -50 ms3negativo_incoherente
700Estado doscientos3no_numerico
750Separada con ;2restriccion_violada
Si la expresión regular matchea, el dato NO es necesariamente válido

fecha_invalida 10.0.0.6 GET / 200 45 tiene seis campos separados por espacios: pasa el patrón sin problema. Que el primero no sea una fecha es una pregunta distinta, y la tiene que hacer tu código.

Es el malentendido más caro de la unidad. Si tu resultado da 796 líneas parseadas, validaste solo la forma. Con las cinco de la capa 3 encontradas, da 791.

Cómo saber si te salió

python comparar.py mi_parseado.csv acceso_parseado.csv

El informe de texto se mira contra el extracto de esta página.

El contrato de salida

acceso_parseado.csv · 791 filas

ColumnaTipoQue lleva
lineaenteroPosicion del elemento en la lista 'lineas', empezando en 1
fechatextoLa marca de tiempo ISO tal cual vino: AAAA-MM-DDTHH:MM:SS
iptextoLa direccion IP tal cual vino
metodotextoUno de: DELETE, GET, HEAD, PATCH, POST, PUT
rutatextoLa ruta pedida, tal cual vino
estadoenteroEl codigo HTTP, entre 100 y 599
msenteroLa duracion en milisegundos, cero o positiva

Orden de las filas: linea ascendente, que es el orden del archivo. Como linea es la posicion en la lista, es unico y el orden es total. Indice: no se escribe: csv.DictWriter no agrega ninguna columna de indice, y con pandas seria to_csv(index=False)

lineas_rechazadas.txt · 21 filas

ColumnaTipoQue lleva

Orden de las filas: linea ascendente: el orden del archivo, no una decision. Indice: no aplica: es texto plano. Con pandas la equivalencia seria to_csv(index=False)

Que hacer con cada defecto

DefectoDondeAccionAdonde va
la linea no tiene seis campos separados por espacios4 lineas: la 60 ('linea invalida'), la 150 (cuatro campos), la 240 (vacia) y la 750 (separada con punto y coma)rechazar. rechazar. Es capa 2: no hay campos que validar todavialineas_rechazadas.txt con motivo restriccion_violada y columna 'linea'
el primer campo no es una fechalinea 420, con el texto literal 'fecha_invalida'rechazar. rechazar. La linea SI tiene seis campos: pasa la capa 2 y solo la ve la capa 3lineas_rechazadas.txt con motivo fecha_invalida
metodo fuera del dominiolinea 330, con FETCHrechazar. rechazar. Los metodos validos son los seis de la listalineas_rechazadas.txt con motivo dominio_desconocido
codigo de estado que no es un numerolinea 700, con 'doscientos'rechazar rechazarlineas_rechazadas.txt con motivo no_numerico
codigo de estado fuera de rangolinea 510, con 999rechazar. rechazar. Los codigos HTTP van de 100 a 599lineas_rechazadas.txt con motivo fuera_de_rango
duracion negativalinea 600, con -50 milisegundosrechazar. rechazar. Es un numero valido y una duracion imposiblelineas_rechazadas.txt con motivo negativo_incoherente
IP con forma incorrecta0 lineas en este archivola la comprobacion va igual: cuatro grupos de hasta tres digitos separados por puntolineas_rechazadas.txt con motivo restriccion_violada
Orden de comprobacion

Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:

  1. capa 2: la linea entera contra el patron de seis campos
  2. capa 3, en este orden: fecha, ip, metodo, estado numerico, estado en rango, ms numerico, ms no negativo
  3. la primera comprobacion que falla es la que se informa: una linea se rechaza una sola vez

La capa 2 va primero porque las capas siguientes necesitan campos para mirar. Y dentro de la capa 3 el orden es el de las columnas: no cambia el resultado en este archivo, pero fija el motivo cuando una linea tenga dos defectos.

Conteos clave

Si estos numeros no te dan, algo se desvio antes de que llegues a comparar los archivos.

lineas de entrada800
acceso_parseado.csv791
lineas_rechazadas.txt21
rechazadas en la capa 24
rechazadas en la capa 35
por motivorestriccion_violada: 4, fecha_invalida: 1, dominio_desconocido: 1, no_numerico: 1, fuera_de_rango: 1, negativo_incoherente: 1

Invariantes

Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:

len(parseado) == 791
len(parseado) + 9 == 800
parseado['linea'].is_unique
parseado['linea'].is_monotonic_increasing
parseado['linea'].between(1, 800).all()
not parseado.isna().any().any()
parseado['estado'].between(100, 599).all()
(parseado['ms'] >= 0).all()
set(parseado['metodo']) <= {'DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'}
parseado['fecha'].str.match(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$').all()
PISTASSi te trabas, abri esto
  • La linea 'fecha_invalida 10.0.0.6 GET / 200 45' TIENE seis campos: el patron la acepta. Si tu resultado la deja pasar, estas confiando en que la regex valida el significado, y solo valida la forma.
  • fullmatch, no match: match se conforma con que el principio coincida, asi que '2026-08-24Txx' pasaria como fecha.
  • El estado 999 es un entero perfectamente valido. Lo rechaza el rango 100 a 599, que sale del protocolo HTTP y no de los datos.
  • Si te dan 796 parseadas, validaste solo la forma. Si te dan 791, encontraste tambien las cinco de la capa 3.

Así tienen que quedar

acceso_parseado.csv (objetivo) · 791 filas, se muestran 5
lineafechaipmetodorutaestadoms
12026-08-24T08:01:0110.0.0.28PUT/login200102
22026-08-24T08:01:1210.0.0.31DELETE/salir200515
32026-08-24T08:01:5310.0.0.31POST/salir201484
42026-08-24T08:02:0710.0.0.28DELETE/api/users200433
52026-08-24T08:03:3610.0.0.2PUT/health20120
lineas_rechazadas.txt (objetivo) · 22 filas, se muestran 5
LINEAS RECHAZADAS
==============================================================================
origen : nginx-simulado
lineas leidas : 800
lineas parseadas : 791
lineas rechazadas : 9
fullmatch, no match

match se conforma con que el principio de la cadena coincida, así que 2026-08-24Tloquesea pasaría como fecha. fullmatch exige que coincida entera.

Para el patrón de la línea completa da lo mismo si la anclás con ^ y $, que es lo que hace el ejemplo de la lección 1.5.

Los rangos salen del protocolo, no de los datos

El 999 es un entero perfectamente válido: ninguna conversión falla. Lo que lo rechaza es que los códigos HTTP van de 100 a 599, y eso lo sabés vos, no el archivo.

Lo mismo con los -50 milisegundos: es un número correcto y una duración imposible.