Comandos SPL¶
Listado de comandos útiles para búsquedas y análisis.
Este documento sirve como guía práctica para aprender y utilizar el lenguaje SPL en Splunk Enterprise.
Los ejemplos están adaptados al laboratorio del curso, que utiliza preferentemente:
Las búsquedas deben ejecutarse desde:
1. Qué es SPL¶
SPL es el lenguaje de búsqueda de Splunk. Permite:
- localizar eventos;
- filtrar resultados;
- transformar campos;
- calcular métricas;
- agrupar eventos;
- crear series temporales;
- extraer información;
- comparar valores;
- preparar datos para dashboards;
- construir alertas;
- investigar incidentes.
Una búsqueda SPL suele comenzar con una búsqueda de eventos y continuar con una cadena de comandos separados por el carácter de tubería:
Ejemplo:
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by uri
| sort - errores
El flujo de esta búsqueda es:
Buscar eventos
↓
Convertir status a número
↓
Filtrar errores
↓
Agrupar por URI
↓
Ordenar de mayor a menor
2. Estructura general de una búsqueda¶
Una búsqueda suele tener estas partes:
Índice¶
Indica dónde buscar:
Tiempo inicial¶
Indica desde cuándo buscar:
Tiempo final¶
Indica hasta cuándo buscar:
Comandos posteriores¶
Transforman o filtran los resultados:
Ejemplo completo¶
3. Reglas básicas antes de empezar¶
Todas las búsquedas del proyecto deben:
- indicar el índice;
- definir un intervalo temporal;
- utilizar nombres reales de campos;
- evitar
index=*salvo diagnóstico; - convertir los valores numéricos cuando sea necesario;
- documentar el objetivo;
- ser reproducibles;
- explicar sus limitaciones.
Ejemplo recomendado¶
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by host uri
| sort - errores
Ejemplo poco recomendable¶
Problemas del segundo ejemplo:
- busca en todos los índices;
- no define un rango temporal;
- depende de una palabra genérica;
- puede devolver demasiados eventos;
- no indica qué pregunta responde;
- puede utilizar muchos recursos;
- no permite interpretar fácilmente el resultado.
4. Búsqueda inicial de eventos¶
Ver eventos del índice¶
Ver los primeros eventos¶
Ver eventos recientes¶
Ver eventos históricos¶
El valor:
permite buscar eventos históricos disponibles en el índice.
No utilices earliest=0 habitualmente en dashboards operativos si no es necesario.
Para un dashboard suele ser preferible un rango más limitado.
Mostrar el evento original¶
Mostrar campos internos importantes¶
5. Campos internos de Splunk¶
Splunk añade campos internos a los eventos.
| Campo | Descripción |
|---|---|
_time |
Tiempo asignado al evento |
_indextime |
Momento en que el evento fue indexado |
_raw |
Evento original |
host |
Host asociado al evento |
source |
Fuente del evento |
sourcetype |
Tipo de fuente |
index |
Índice donde se encuentra el evento |
linecount |
Número de líneas del evento, cuando aplica |
Revisar el tiempo del evento¶
Comparar _time e _indextime¶
index=curso earliest=0 latest=now
| eval retraso_ingesta=_indextime-_time
| table _time _indextime retraso_ingesta host uri status
| head 20
Un retraso elevado puede ser normal si se cargan archivos históricos.
También puede indicar:
- retraso de entrega;
- problema de zona horaria;
- parsing incorrecto;
- datos generados antes de ser ingeridos.
Formatear _time¶
index=curso earliest=0 latest=now
| eval fecha=strftime(_time, "%Y-%m-%d %H:%M:%S")
| table fecha host method status uri
| head 20
6. Comando search¶
El comando search filtra eventos.
Filtrar por código HTTP¶
Filtrar por host¶
Filtrar por URI¶
Combinar condiciones¶
Utilizar OR¶
Utilizar paréntesis¶
Buscar una palabra¶
Buscar en un campo concreto¶
Cuando sea posible, es preferible utilizar filtros específicos de campo.
7. Comando where¶
where filtra resultados utilizando expresiones.
Es especialmente útil después de crear campos con eval.
Filtrar códigos superiores o iguales a 400¶
Filtrar errores HTTP 500¶
Filtrar por duración¶
Filtrar valores no nulos¶
Filtrar valores nulos¶
Combinar condiciones¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=500 AND host="web-01"
Diferencia entre search y where¶
Utiliza search para filtros sencillos sobre eventos:
Utiliza where cuando necesites expresiones o cálculos:
8. Comando table¶
table selecciona los campos que quieres mostrar.
Mostrar campos concretos¶
Mostrar campos calculados¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| table _time host status status_num uri
Tabla de eventos recientes¶
Evita utilizar:
cuando no sea necesario.
Una tabla con todos los campos puede ser difícil de leer y consumir más recursos.
9. Comando fields¶
fields incluye o excluye campos.
Mantener campos concretos¶
Excluir un campo¶
Diferencia entre table y fields¶
table está orientado a presentar una tabla final.
fields se utiliza para controlar los campos disponibles durante la búsqueda y
puede ayudar a reducir el volumen de datos procesados.
10. Comando head¶
head devuelve los primeros resultados.
Primeros diez eventos¶
Primeros resultados después de ordenar¶
En este caso, head devuelve las diez URI con más errores.
11. Comando tail¶
tail devuelve los últimos resultados.
Para ver los eventos más recientes, suele ser más claro:
12. Comando sort¶
sort ordena resultados.
Orden ascendente¶
Orden descendente¶
Ordenar por varios campos¶
Ordenar por latencia¶
index=curso earliest=0 latest=now
| eval tiempo_ms=tonumber(response_time)
| stats avg(tiempo_ms) as media_ms by uri
| sort - media_ms
13. Comando eval¶
eval crea campos calculados y transforma valores.
Convertir un campo a número¶
Crear una bandera de error¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| eval es_error=if(status_num>=400, 1, 0)
Crear una bandera de HTTP 500¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| eval es_500=if(status_num=500, 1, 0)
Crear una clasificación HTTP¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| eval clase_http=case(
status_num>=500, "5xx",
status_num>=400, "4xx",
status_num>=300, "3xx",
status_num>=200, "2xx",
true(), "otro"
)
Crear una clasificación sencilla¶
Calcular porcentaje¶
index=curso earliest=0 latest=now
| stats
count as total
count(eval(status>=400)) as errores
| eval porcentaje=round(errores*100/total, 2)
Es más seguro convertir primero el campo:
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as total
count(eval(status_num>=400)) as errores
| eval porcentaje=if(
total>0,
round(errores*100/total, 2),
0
)
Formatear fechas¶
Calcular segundos desde el evento¶
14. Funciones habituales de eval¶
if¶
case¶
| eval clase=case(
status_num>=500, "Error de servidor",
status_num>=400, "Error de cliente",
status_num>=300, "Redirección",
status_num>=200, "Correcta",
true(), "Desconocida"
)
coalesce¶
Devuelve el primer campo no nulo.
isnull¶
isnotnull¶
len¶
lower¶
upper¶
round¶
tonumber¶
tostring¶
15. Comando stats¶
stats calcula agregaciones sobre los eventos.
Contar eventos¶
Contar con alias¶
Contar por host¶
Contar por código HTTP¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats count as peticiones by status_num
Contar por host y URI¶
Calcular varias métricas¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as peticiones
count(eval(status_num>=400)) as errores
dc(uri) as uri_distintas
dc(host) as hosts_distintos
Media de latencia¶
index=curso earliest=0 latest=now
| eval tiempo_ms=tonumber(response_time)
| stats avg(tiempo_ms) as media_ms
Mínimo y máximo¶
index=curso earliest=0 latest=now
| eval tiempo_ms=tonumber(response_time)
| stats
min(tiempo_ms) as minimo_ms
max(tiempo_ms) as maximo_ms
Mediana y percentiles¶
index=curso earliest=0 latest=now
| eval tiempo_ms=tonumber(response_time)
| stats
median(tiempo_ms) as mediana_ms
perc95(tiempo_ms) as p95_ms
perc99(tiempo_ms) as p99_ms
Errores por URI¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by uri
| sort - errores
16. Funciones estadísticas condicionales¶
Puedes utilizar count(eval(...)) dentro de stats.
Contar errores¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats count(eval(status_num>=400)) as errores
Contar HTTP 500¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats count(eval(status_num=500)) as errores_500
Comparar correctas y errores¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as total
count(eval(status_num<400)) as correctas
count(eval(status_num>=400)) as errores
Calcular tasa de error¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as total
count(eval(status_num>=400)) as errores
| eval tasa_error=if(
total>0,
round(errores/total*100, 2),
0
)
17. Comando timechart¶
timechart crea series temporales.
Peticiones por minuto¶
Peticiones por cinco minutos¶
Errores por minuto¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| timechart span=1m count as errores
Correctas frente a errores¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| eval tipo=if(status_num>=400, "Error", "Correcta")
| timechart span=1m count by tipo
HTTP 500 por minuto¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num=500
| timechart span=1m count as errores_500
Evolución por host¶
Evolución por código HTTP¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| timechart span=1m count by status_num
Interpretación¶
Busca:
- picos de tráfico;
- aumento de errores;
- periodos sin eventos;
- hosts con comportamiento diferente;
- coincidencia entre volumen elevado y errores;
- aparición repentina de HTTP
5xx.
Una serie temporal muestra cuándo ocurre algo, pero no necesariamente por qué.
18. Comando chart¶
chart crea tablas o gráficos agrupados por uno o dos campos.
Peticiones por host y método¶
Errores por host y código¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| chart count over host by status_num
Utiliza chart cuando quieras comparar categorías.
Utiliza timechart cuando el eje principal sea el tiempo.
19. Comando eventstats¶
eventstats calcula estadísticas y las añade a cada evento sin eliminar el
detalle original.
Añadir el total de eventos a cada evento¶
index=curso earliest=0 latest=now
| eventstats count as total_eventos
| table _time host status uri total_eventos
| head 20
Añadir el total por host¶
index=curso earliest=0 latest=now
| eventstats count as peticiones_host by host
| table _time host uri peticiones_host
| head 20
Calcular porcentaje de cada host¶
index=curso earliest=0 latest=now
| eventstats count as total_eventos
| eventstats count as eventos_host by host
| eval porcentaje_host=round(eventos_host*100/total_eventos, 2)
| table _time host uri eventos_host total_eventos porcentaje_host
| head 20
20. Comando streamstats¶
streamstats calcula valores acumulados respetando el orden de los eventos.
Contador acumulado¶
index=curso earliest=0 latest=now
| sort 0 _time
| streamstats count as numero_evento
| table _time numero_evento host status uri
Errores acumulados¶
index=curso earliest=0 latest=now
| sort 0 _time
| eval status_num=tonumber(status)
| eval es_error=if(status_num>=400, 1, 0)
| streamstats sum(es_error) as errores_acumulados
| table _time status uri errores_acumulados
El orden temporal es importante. Si utilizas streamstats, comprueba que los
eventos están ordenados como necesitas.
21. Comando dedup¶
dedup elimina eventos duplicados según uno o varios campos.
Eliminar URI repetidas¶
Eliminar duplicados por varios campos¶
Mantener una muestra por host¶
Utiliza dedup con cuidado. Que dos eventos compartan los mismos campos visibles
no significa necesariamente que sean duplicados reales.
22. Comando rename¶
rename cambia el nombre de un campo en los resultados.
Renombrar un campo¶
Renombrar métricas¶
index=curso earliest=0 latest=now
| stats count as total_eventos
| rename total_eventos as peticiones
Los nombres deben ser claros para los usuarios del dashboard.
23. Comando rex¶
rex extrae o transforma valores mediante expresiones regulares.
Extraer el recurso principal de una URI¶
Para una URI como:
el campo recurso podría contener:
Extraer un identificador de una URI¶
Extraer un código desde _raw¶
index=curso earliest=0 latest=now
| rex field=_raw "status=(?<status_extraido>[0-9]{3})"
| table _raw status_extraido
Utiliza rex cuando el campo no exista o cuando necesites extraer una parte
concreta de una cadena.
No utilices expresiones regulares complejas si el campo ya está disponible.
24. Comando spath¶
spath extrae campos de estructuras JSON o XML compatibles.
Ejemplo conceptual con JSON¶
Si un evento contiene:
Puedes utilizar:
Extraer un campo concreto¶
index=curso earliest=0 latest=now
| spath input=_raw path=response.status output=status_json
| table status_json
La sintaxis exacta depende de la estructura real del evento.
25. Comando lookup¶
lookup permite enriquecer eventos con información externa.
Ejemplo conceptual:
index=curso earliest=0 latest=now
| lookup activos.csv host OUTPUT owner environment
| stats count by environment owner
El lookup podría contener:
Uso práctico¶
Puedes utilizar un lookup para añadir:
- propietario del host;
- entorno;
- aplicación;
- ubicación;
- criticidad;
- equipo responsable.
Documenta siempre:
- nombre del lookup;
- ubicación;
- campos de unión;
- campos añadidos;
- propietario;
- fecha de actualización.
26. Comando fillnull¶
fillnull sustituye valores nulos.
Sustituir nulos de host¶
Sustituir nulos de latencia¶
Utiliza fillnull con cuidado. Un cero puede significar “sin dato” y no
necesariamente “duración cero”.
En muchos casos es mejor conservar el valor nulo y documentar su significado.
27. Comando replace¶
replace sustituye valores de un campo.
Normalizar métodos¶
Normalizar nombres de host¶
Utilízalo únicamente cuando conozcas la equivalencia correcta.
28. Comando format¶
format convierte resultados en una expresión de búsqueda.
Ejemplo conceptual:
Es un comando avanzado. Úsalo solo si entiendes la consulta generada.
29. Comando return¶
return devuelve valores concretos de los resultados.
Ejemplo conceptual:
Su uso suele aparecer en búsquedas encadenadas o subsearches.
30. Subsearches¶
Una subsearch está delimitada por corchetes:
Ejemplo conceptual¶
Buscar eventos de los hosts que hayan generado HTTP 500:
index=curso earliest=0 latest=now
[
search index=curso earliest=0 latest=now status=500
| fields host
| format
]
Las subsearches pueden ser útiles, pero también pueden aumentar la complejidad y el coste de una búsqueda.
Para los ejercicios iniciales, suele ser preferible una búsqueda directa y clara.
31. Análisis de códigos HTTP¶
Distribución por código¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats count as peticiones by status_num
| sort status_num
Clasificación por familia¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| eval familia=case(
status_num>=500, "5xx",
status_num>=400, "4xx",
status_num>=300, "3xx",
status_num>=200, "2xx",
true(), "otro"
)
| stats count as peticiones by familia
| sort familia
Errores por host¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by host
| sort - errores
Errores por URI¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by uri
| sort - errores
| head 10
HTTP 500 por minuto¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num=500
| timechart span=1m count as errores_500
32. Análisis de porcentaje de error¶
Porcentaje global¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as total
count(eval(status_num>=400)) as errores
| eval porcentaje_error=if(
total>0,
round(errores*100/total, 2),
0
)
Porcentaje por host¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as peticiones
count(eval(status_num>=400)) as errores
by host
| eval porcentaje_error=if(
peticiones>0,
round(errores*100/peticiones, 2),
0
)
| sort - porcentaje_error
Porcentaje por URI¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as peticiones
count(eval(status_num>=400)) as errores
by uri
| eval porcentaje_error=if(
peticiones>0,
round(errores*100/peticiones, 2),
0
)
| sort - porcentaje_error
| head 10
Un ranking por errores absolutos y un ranking por porcentaje pueden ser diferentes. Ambos deben interpretarse con cuidado.
33. Análisis de latencia¶
Este análisis requiere un campo como response_time, duration o latency.
Convertir la latencia¶
Latencia media por URI¶
index=curso earliest=0 latest=now
| eval tiempo_ms=tonumber(response_time)
| where isnotnull(tiempo_ms)
| stats avg(tiempo_ms) as media_ms by uri
| sort - media_ms
Percentil 95 por URI¶
index=curso earliest=0 latest=now
| eval tiempo_ms=tonumber(response_time)
| where isnotnull(tiempo_ms)
| stats
avg(tiempo_ms) as media_ms
median(tiempo_ms) as mediana_ms
perc95(tiempo_ms) as p95_ms
by uri
| sort - p95_ms
Respuestas superiores a un segundo¶
index=curso earliest=0 latest=now
| eval tiempo_ms=tonumber(response_time)
| where tiempo_ms>1000
| stats count as respuestas_lentas by uri
| sort - respuestas_lentas
Si no existe el campo de latencia, documenta la limitación y no utilices un panel titulado “URL más lentas”.
34. Análisis por IP¶
Este análisis requiere clientip, src_ip o un campo equivalente.
IP con más errores¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by clientip
| sort - errores
| head 10
Peticiones y errores por IP¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as peticiones
count(eval(status_num>=400)) as errores
by clientip
| eval porcentaje_error=if(
peticiones>0,
round(errores*100/peticiones, 2),
0
)
| sort - errores
IP con actividad elevada en cinco minutos¶
index=curso earliest=-5m latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by clientip
| where errores>=5
| sort - errores
Si no existe IP, utiliza host como alternativa, pero cambia el título y documenta
la limitación.
35. Comando transaction¶
transaction agrupa eventos relacionados.
Ejemplo conceptual:
index=curso earliest=0 latest=now
| transaction clientip maxspan=5m
| table clientip duration eventcount
transaction puede consumir muchos recursos, especialmente con grandes volúmenes.
Antes de utilizarlo, considera si puedes resolver el problema mediante:
stats;streamstats;eventstats;transactioncon campos de agrupación precisos;- un identificador de correlación.
No utilices transaction como primera opción en un dashboard de producción sin
justificarlo.
36. Comando search con comodines¶
URI que comienza por /api¶
Hosts de una familia¶
Métodos concretos¶
Comprueba que los valores reales coinciden exactamente con el patrón utilizado.
37. Comando IN¶
IN permite comprobar varios valores.
Códigos concretos¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num IN (400, 401, 403, 404)
Hosts concretos¶
Métodos concretos¶
38. Comando like¶
like permite comparar patrones.
En expresiones like:
representa una secuencia de caracteres.
39. Diagnóstico de campos ausentes¶
Revisar campos disponibles¶
Revisar eventos originales¶
Comprobar IP¶
Comprobar latencia¶
Comprobar campos nulos¶
index=curso earliest=0 latest=now
| eval falta_ip=if(isnull(clientip), 1, 0)
| eval falta_latencia=if(isnull(response_time), 1, 0)
| stats
sum(falta_ip) as eventos_sin_ip
sum(falta_latencia) as eventos_sin_latencia
40. Diagnóstico de una búsqueda sin resultados¶
Si esta búsqueda no devuelve resultados:
sigue este orden.
Paso 1: comprobar que el índice contiene eventos¶
Paso 2: comprobar el rango temporal¶
Paso 3: comprobar los valores de status¶
Paso 4: convertir el campo¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats count by status status_num
Paso 5: realizar el filtro¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num=500
| stats count
Paso 6: revisar permisos¶
Comprueba que el usuario puede leer el índice curso.
Paso 7: revisar logs internos¶
index=_internal earliest=-30m latest=now
| search log_level=error OR log_level=warn
| table _time host component log_level message
| sort - _time
41. Búsquedas para dashboards¶
Panel de total de peticiones¶
Panel de total de errores¶
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| stats count(eval(status_num>=400)) as errores
Panel de porcentaje de error¶
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| stats
count as peticiones
count(eval(status_num>=400)) as errores
| eval porcentaje_error=if(
peticiones>0,
round(errores*100/peticiones, 2),
0
)
Panel de peticiones por minuto¶
Panel de errores por código¶
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| stats count as errores by status_num
| sort status_num
Panel de URI con más errores¶
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by uri
| sort - errores
| head 10
Panel de host con más errores¶
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by host
| sort - errores
| head 10
42. Búsquedas para alertas¶
Cinco HTTP 500 en cinco minutos¶
index=curso earliest=-5m latest=now
| eval status_num=tonumber(status)
| stats count(eval(status_num=500)) as errores_500
| where errores_500>=5
Cinco errores de cualquier tipo en cinco minutos¶
index=curso earliest=-5m latest=now
| eval status_num=tonumber(status)
| stats count(eval(status_num>=400)) as errores
| where errores>=5
Host con cinco errores¶
index=curso earliest=-5m latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by host
| where errores>=5
URI con cinco errores¶
index=curso earliest=-5m latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by uri
| where errores>=5
Documenta siempre:
- por qué se eligió el umbral;
- por qué se eligió la ventana;
- con qué frecuencia se ejecuta;
- qué acción se realiza;
- cómo se evita la repetición de avisos.
43. Buenas prácticas de rendimiento¶
Filtrar por índice y tiempo¶
Recomendado:
Menos recomendable:
Filtrar pronto¶
Recomendado:
index=curso earliest=-24h latest=now
| eval status_num=tonumber(status)
| where status_num=500
| stats count by uri
Evitar campos innecesarios¶
Recomendado:
Limitar rankings¶
Evitar table *¶
Utiliza solo los campos necesarios:
Evitar transaction sin necesidad¶
Considera primero:
o:
Revisar búsquedas programadas¶
Los reportes y alertas frecuentes pueden consumir recursos. Documenta:
- frecuencia;
- intervalo;
- cantidad aproximada de eventos;
- necesidad operativa;
- posible solapamiento;
- acción.
44. Ejercicios progresivos¶
Ejercicio 1: contar eventos¶
Preguntas:
- ¿Cuántos eventos existen?
- ¿Qué rango temporal tienen?
- ¿El número coincide con el archivo original?
Ejercicio 2: contar por host¶
Preguntas:
- ¿Qué host tiene más eventos?
- ¿Hay hosts inesperados?
- ¿Qué implicación tendría un host sin datos?
Ejercicio 3: clasificar códigos¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| eval familia=case(
status_num>=500, "5xx",
status_num>=400, "4xx",
status_num>=300, "3xx",
status_num>=200, "2xx",
true(), "otro"
)
| stats count by familia
Preguntas:
- ¿Qué familia domina?
- ¿Hay respuestas
5xx? - ¿Hay valores
otro?
Ejercicio 4: URI con más errores¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| where status_num>=400
| stats count as errores by uri
| sort - errores
| head 10
Preguntas:
- ¿Qué URI requiere investigación?
- ¿Se trata de
4xxo5xx? - ¿La URI tiene mucho tráfico total?
Ejercicio 5: evolución temporal¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| eval resultado=if(status_num>=400, "Error", "Correcta")
| timechart span=1m count by resultado
Preguntas:
- ¿Cuándo aparecen los errores?
- ¿Coinciden con un pico de tráfico?
- ¿Hay intervalos sin actividad?
Ejercicio 6: error relativo por URI¶
index=curso earliest=0 latest=now
| eval status_num=tonumber(status)
| stats
count as peticiones
count(eval(status_num>=400)) as errores
by uri
| eval porcentaje_error=round(errores*100/peticiones, 2)
| sort - porcentaje_error
Preguntas:
- ¿La URI con más errores absolutos es también la de mayor porcentaje?
- ¿Qué métrica es más útil para priorizar?
- ¿Qué volumen mínimo necesitarías para confiar en el porcentaje?
Ejercicio 7: alerta¶
index=curso earliest=-5m latest=now
| eval status_num=tonumber(status)
| stats count(eval(status_num=500)) as errores_500
| where errores_500>=5
Preguntas:
- ¿Se puede probar con datos históricos?
- ¿Qué ocurre si los eventos tienen timestamps antiguos?
- ¿Cómo evitarías avisos repetidos?
45. Plantilla para documentar una búsqueda¶
Utiliza esta plantilla en los entregables:
#### Nombre de la búsqueda
###### Objetivo
Describir la pregunta operativa que responde.
###### SPL
```spl
index=curso earliest=-24h latest=now
| stats count by host
Índice¶
Intervalo temporal¶
Campos utilizados¶
- host
Resultado esperado¶
Describir el resultado.
Resultado observado¶
Completar con el resultado real.
Interpretación¶
Explicar qué significa.
Limitaciones¶
Indicar qué no puede demostrarse.
Fecha de validación¶
Completar la fecha.
---
## 46. Errores habituales de los asistentes
#### Error: no indicar el índice
Incorrecto:
```spl
| stats count
Correcto:
Error: comparar texto con números¶
Poco recomendable:
Recomendado:
Error: utilizar datos históricos con tiempo relativo¶
Si los eventos son del 1 de enero de 2026, esta búsqueda puede no devolver datos:
Utiliza para la práctica:
Error: tratar host como IP¶
host y clientip representan entidades diferentes.
Error: afirmar que una URI es lenta sin latencia¶
Sin response_time, duration o latency, no puedes calcular rendimiento.
Error: crear una alerta sin probar la consulta¶
Ejecuta primero la búsqueda manualmente y documenta el resultado.
Error: utilizar table *¶
Selecciona únicamente los campos necesarios.
Error: confiar únicamente en una visualización¶
Un gráfico no sustituye la validación de la consulta.
47. Checklist de SPL¶
Antes de ejecutar¶
- El índice es correcto.
- El intervalo temporal es correcto.
- Los campos existen.
- Los campos numéricos se convierten.
- La consulta responde una pregunta concreta.
- El usuario tiene permisos.
Después de ejecutar¶
- El resultado tiene sentido.
- El número de eventos es razonable.
- No hay valores inesperados.
- Se han revisado los campos nulos.
- La consulta puede reutilizarse.
- Se han documentado las limitaciones.
Antes de guardar¶
- El nombre es claro.
- La descripción está completa.
- La aplicación es correcta.
- Los permisos son adecuados.
- El propietario está documentado.
- La consulta no depende de valores temporales incorrectos.