Libro 18 · Artefacto 6

Artefacto 6 · Monitorización · Fase 4 · la herramienta

La sala de lectura

La bitácora escribe; alguien tiene que leer. El Explorador de registros de Google Cloud Logging es la sala donde se lee todo lo que el proyecto cuenta de sí mismo — y como toda sala de lectura seria, tiene sus normas: qué hay en las estanterías, cómo se pide un libro, y cómo se interpreta lo que pone. Este libro es el manual de uso de esa sala, escrito el día que la estrenamos.

23 de julio de 2026 · consola de GCP · proyecto dprag-dobe · primera lectura real: 3 líneas, 2 advertencias y una factura

01Qué hay en las estanterías

Lo primero que sorprende al entrar: la sala no guarda solo tus logs. Cloud Logging es el archivo central de todo el proyecto, y ahí desemboca todo lo que cualquier pieza de GCP tiene que contar:

Quién escribeQué cuentaEjemplo real del proyecto
Tus contenedoresel stdout de tu códigolas líneas de bitacora(), el «✅ Agente listo»
Cloud Run (la plataforma)peticiones HTTP, arranques, escalacada request con su código de estado, visto desde fuera
Cloud Buildla construcción de imágenesel build de cada gcloud run deploy --source
Logs de auditoríaquién tocó qué en el proyectola consulta precargada que había que borrar era justo esto
Secret Manageraccesos a secretoscada vez que una revisión lee APP_TOKEN_AGENTE

Por eso ninguna consulta útil empieza «enséñamelo todo»: el todo es un océano. Leer bien es, antes que nada, filtrar bien.

02Anatomía de la pantalla

El Explorador tiene cuatro zonas, y cada una responde una pregunta distinta:

El editor de consultas (arriba)

Donde se escribe el filtro, en un lenguaje propio (LQL, Logging Query Language). Es la petición al bibliotecario: «tráeme solo esto». Se puede escribir a mano o construir clicando — los desplegables de recurso/gravedad y el panel de campos escriben LQL por ti en esta misma caja. Verlo aparecer ahí es la mejor forma de aprender el lenguaje.

El cronograma (la gráfica)

El electrocardiograma del servicio: el tiempo en horizontal, el volumen de líneas en vertical, coloreado por gravedad. No se lee línea a línea — se lee de un vistazo: ¿hay pulso? ¿hay picos? ¿una racha naranja de errores a las 3 de la mañana? El cronograma responde «¿cuándo pasó algo raro?» antes de que sepas qué pasó. Arrastrar sobre él acota el rango de tiempo: es zoom, no decoración.

La tabla de resultados

Una fila por línea de log que pasó el filtro: Gravedad (el severity del JSON convertido en icono — ⚠️ naranja los WARNING, ℹ️ azul los INFO, 🔴 los ERROR), Hora (¡ojo, en tu zona horaria! — ver capítulo 05) y Resumen (el JSON crudo, truncado). La flechita ▸ de cada fila despliega el registro completo en árbol; dentro, el tesoro es el bloque jsonPayload: tus diez campos, uno a uno.

El panel «Campos» (izquierda)

El más infravalorado. La consola aprende de los logs que está mostrando y te ofrece sus valores como facetas clicables: «Gravedad: Advertencia 2 · Información 1», «Service name: dobe-agente»… Dos usos: como atajo (clicar añade el filtro al editor) y como resumen instantáneo — ese recuento por gravedad ya es media consulta del DoD respondida sin escribir nada.

03El embudo de tres cedazos

La consulta con la que se estrenó la sala, línea a línea — con un AND implícito entre líneas (un registro debe cumplir las tres):

resource.type="cloud_run_revision"      # cedazo 1: la plataforma
resource.labels.service_name="dobe-agente"   # cedazo 2: el servicio
jsonPayload.evento="peticion"           # cedazo 3: la línea — y este es TUYO

Cedazo 1 — plataforma. De todo el océano del capítulo 01, solo lo emitido por contenedores de Cloud Run. El nombre fino importa: cloud_run_revision, no «service» — quien ejecuta código y escribe logs es la revisión concreta (la 00004-fvk del libro 17), no el servicio abstracto.

Cedazo 2 — servicio. El proyecto tiene dos servicios (dobe-agente y dobe-rag); sin esta línea sus logs saldrían mezclados. Es la misma etiqueta que un día separará las consultas del copiloto de llamadas de las del agente.

Cedazo 3 — la línea, el cedazo de autor. Dentro del stdout del agente conviven la bitácora, el saludo de arranque, los logs de uvicorn… El campo evento: "peticion" lo inventó el autor de la bitácora exactamente para esto: es la marca de agua que separa las mediciones del ruido de consola. La observabilidad se diseña en la escritura para que exista en la lectura.

La idea que se lleva uno Filtrar en Cloud Logging es la misma lógica que los filtros de metadata en Pinecone: cada nivel de etiqueta rebana el océano por una dimensión distinta (plataforma → servicio → tipo de línea), y la dimensión más valiosa es la que definiste tú al diseñar el dato.

04La frontera: jsonPayload contra textPayload

Cuando Cloud Logging recoge una línea de stdout, la huele. Si es JSON válido, la desmonta e indexa campo a campo bajo jsonPayload — y cada campo se vuelve consultable (jsonPayload.estado, jsonPayload.coste_usd…) y algunos hasta cambian el comportamiento: el campo severity asciende a nivel oficial del registro y pinta el icono de gravedad. Si la línea es texto normal, queda entera como textPayload: una cadena opaca donde solo se pueden buscar palabras.

# El mismo print, dos destinos:
print("✅ Agente listo.")                    → textPayload (prosa: solo se busca)
print(json.dumps({"evento": "peticion"...})) → jsonPayload (dato: se consulta)

Esta es la frontera del libro 15 vista desde el otro lado: en la escritura parecía una elección estética (¿prosa o JSON?); en la sala de lectura se revela como la diferencia entre buscar y consultar. Y no hubo que instalar nada para cruzarla: el contrato «si imprimes JSON, te indexo» viene de serie.

05Los dos relojes

Una misma petición, dos horas: la columna Hora decía 13:30:44 y dentro del JSON, hora_utc: 11:30:44. No es un error — son dos relojes con dos oficios. La consola traduce a la zona del espectador (Madrid, UTC+2 en verano) porque los humanos leen en hora local; la bitácora guarda UTC porque los sistemas comparan en hora universal.

La regla práctica: se escribe en UTC, se lee en local — y cuando dos fuentes discrepen en dos horas exactas, antes de sospechar un bug, sospecha del reloj. (Y un matiz de ingeniería: aunque la consola ya traduce, el hora_utc propio no sobra — si un día los logs se exportan a un CSV o a otra herramienta, el campo viaja con el dato; la traducción de la consola, no.)

06Leer lo que no está

La lectura más fina de la primera sesión no fue un campo — fue una ausencia. Las dos líneas de los 401 no llevan coste_usd, ni llamadas_llm, ni completado. Un lector ingenuo diría «datos incompletos». El lector que diseñó la bitácora sabe que esa ausencia es un mensaje: el portero rechazó al gorrón antes de que el agente arrancara — la cartera ni se abrió.

Lo que se veLo que significa
WARNING con estado: 401 y sin campos de costeintento rechazado gratis: el token no pasó, el LLM ni se enteró
INFO con completado: true y coste_usdpetición redonda, con su factura del taxi
INFO con completado: falsefallo blando: el 200 dice «terminé», este campo dice «pero sin acabar la faena» (guardarraíl de turnos)
ERROR con estado: 500 y coste_usdfallo duro que costó dinero — el arreglo de Wald del libro 16 en acción
Muchos 401 seguidos en el cronogramaalguien insiste contra el portero; molesto quizá, caro no
En una bitácora bien diseñada, hasta el campo que falta está diciendo algo. La interpretación no es leer los datos: es saber qué esperaba encontrar y notar la diferencia.

07Recetario de consultas

Las recetas que interrogan la bitácora del agente. Todas comparten los dos primeros cedazos (recurso + servicio); se lista solo lo que cambia. Los operadores del LQL que usan: = igual, != distinto, >= / < comparación numérica, :* «el campo existe», y AND/OR/NOT para combinar.

PreguntaSe añade al embudo
Solo las peticiones redondasjsonPayload.estado=200
Fallos duros (DoD)jsonPayload.estado>=500
Fallos blandos (DoD)jsonPayload.completado=false
Todo lo que no fue bienseverity>=WARNING
Peticiones lentas (más de 15 s)jsonPayload.latencia_ms>=15000
Las que costaron dinerojsonPayload.coste_usd:*
Las del futuro copiloto, apartejsonPayload.ruta="/preparar-llamada"

Y la receta de interpretación general, en orden: (1) cronograma — ¿cuándo?; (2) panel de campos — ¿cuánto de cada?; (3) filtro fino — ¿cuáles exactamente?; (4) desplegar el registro — ¿qué pasó en esta?

08Lo que la sala no hace (y dónde está el límite honesto)

El Explorador filtra, muestra y cuenta — pero no agrega. Puede decirte «hay 47 líneas con estado 200», no «la media de su coste_usd es $0.0021». Para las preguntas de agregación hay tres caminos, de menos a más maquinaria:

El aviso del archivero Las estanterías no son infinitas: los logs del bucket por defecto se guardan 30 días y luego se purgan. Para la operación diaria basta y sobra; si algún día hace falta historia larga (¿cuánto costó el agente este trimestre?), el camino es exportarlos (un sink hacia BigQuery o Storage) — decisión consciente, con su coste, que hoy no toca.

09Las palabras que se lleva el CV

TérminoLo que significa aquí
Logs Explorer / LQLla sala de lectura y su lenguaje de filtros; los desplegables escriben LQL por ti
jsonPayload vs textPayloaddato consultable campo a campo vs prosa opaca; la frontera la decide tu print
severity mappingel campo severity del JSON asciende a nivel oficial del registro (iconos, filtros)
facets (panel Campos)la consola aprende los valores de tus logs y los ofrece como filtros clicables con recuento
log-based metricsdestilar números de las líneas y graficarlos/alertarlos en Monitoring; el escalón siguiente
log retention / sink30 días por defecto; la historia larga se exporta a propósito, no se hereda gratis

10Lo que queda abierto

· · ·