Analisi Log con Loki
Accesso a Loki via Grafana Explore
Loki non ha una propria UI: si accede esclusivamente tramite Grafana Explore.
- Aprire Grafana (
https://grafana.platform.sellogic.cloud) - Navigare a Explore (icona bussola nella sidebar)
- Selezionare la data source Loki dal dropdown in alto
- Inserire la query LogQL nella barra di ricerca
Da qualsiasi dashboard Grafana, cliccare sull'icona Explore di un pannello per aprirlo direttamente in Explore con il contesto precompilato.
Formato Log Strutturati
I servizi Moleculer producono log in formato JSON strutturato su stdout:
{
"timestamp": "2026-03-29T10:15:32.456Z",
"level": "info",
"nodeID": "core-7b8f9d6c4-x2k9m",
"service": "orders",
"action": "orders.create",
"requestID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"correlationId": "req-98765-aaaa-bbbb",
"tenantId": "ristorante_roma_01",
"duration": 142,
"msg": "Ordine creato con successo",
"orderId": "ORD-2026-001234"
}
Label automatiche aggiunte da Promtail:
namespace:pos-enterprisepod: nome del pod Kubernetescontainer: nome del containernode_type: tipo di nodo Moleculer (es.core,gateway,commerce)stream:stdoutostderr
Query LogQL: Fondamentali
Sintassi Base
{label="valore"} |= "testo da cercare"
- Stream selector
{...}: filtra per label (obbligatorio) - Pipeline:
|=(contiene),!=(non contiene),|~(regex),!~(non regex) - Parser:
| jsonper estrarre campi dal JSON strutturato
Filtro per Servizio
# Tutti i log del servizio orders
{namespace="pos-enterprise", node_type="core"} | json | service="orders"
# Log del gateway API
{namespace="pos-enterprise", node_type="gateway"}
# Log del servizio auth (Keycloak integration)
{namespace="pos-enterprise", node_type="auth"}
Filtro per Tenant
# Tutti i log di un tenant specifico
{namespace="pos-enterprise"} | json | tenantId="ristorante_roma_01"
# Log di un tenant con errori
{namespace="pos-enterprise"} | json | tenantId="ristorante_roma_01" | level="error"
Filtro per Livello di Errore
# Solo errori
{namespace="pos-enterprise"} | json | level="error"
# Errori e warning
{namespace="pos-enterprise"} | json | level=~"error|warn"
# Escludere i log di health check (rumorosi)
{namespace="pos-enterprise"} |= "" != "/public/health"
Filtro per Correlation ID
# Tracciare una richiesta specifica attraverso tutti i servizi
{namespace="pos-enterprise"} | json | correlationId="req-98765-aaaa-bbbb"
# Tracciare per requestID Moleculer
{namespace="pos-enterprise"} |= "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
Scenari di Analisi Comuni
1. Trovare Errori Recenti per Servizio
# Errori negli ultimi 30 minuti per il servizio commerce
{namespace="pos-enterprise", node_type="commerce"} | json | level="error"
# Con conteggio per tipo di errore
sum by (action, msg) (
count_over_time(
{namespace="pos-enterprise", node_type="commerce"} | json | level="error" [30m]
)
)
2. Tracciare il Flusso di una Richiesta
Quando una richiesta attraversa piu servizi, il correlationId permette di ricostruire il percorso completo:
# Passo 1: trovare il correlationId dall'errore
{namespace="pos-enterprise"} | json | level="error" | line_format "{{.correlationId}} | {{.service}}.{{.action}} | {{.msg}}"
# Passo 2: seguire il correlationId
{namespace="pos-enterprise"} | json | correlationId="<id-trovato>" | line_format "{{.timestamp}} [{{.service}}] {{.action}} - {{.msg}} ({{.duration}}ms)"
3. Analisi Latenza da Log
# Richieste con durata > 1000ms
{namespace="pos-enterprise"} | json | duration > 1000 | line_format "{{.service}}.{{.action}} {{.duration}}ms tenant={{.tenantId}}"
# Media durata per azione (metrica da log)
avg_over_time(
{namespace="pos-enterprise"} | json | unwrap duration [5m]
) by (service, action)
4. Errori di Connessione Database
# Errori MongoDB
{namespace="pos-enterprise"} |= "MongoError" or |= "MongoServerError" or |= "pool" |= "error"
# Errori Redis
{namespace="pos-enterprise"} |= "CROSSSLOT" or |= "ECONNREFUSED" or |= "Redis" | json | level="error"
# Errori NATS
{namespace="pos-enterprise"} |= "NATS" |= "disconnect" or |= "reconnect"
5. Audit delle Operazioni Sensibili
# Login e accessi support
{namespace="pos-enterprise", node_type="auth"} | json | action=~".*login.*|.*authenticate.*"
# Sessioni support
{namespace="pos-enterprise"} | json | service="support-access" | line_format "{{.timestamp}} [{{.action}}] tech={{.technicianId}} tenant={{.tenantId}}"
Metriche Derivate dai Log
LogQL supporta query di tipo metrica per creare dashboard:
# Rate di errori al secondo per servizio
sum by (service) (
rate({namespace="pos-enterprise"} | json | level="error" [5m])
)
# Conteggio log per livello negli ultimi 15 minuti
sum by (level) (
count_over_time({namespace="pos-enterprise"} | json [15m])
)
# Percentile 95 della durata delle richieste (da log)
quantile_over_time(0.95,
{namespace="pos-enterprise"} | json | unwrap duration [5m]
) by (service)
Retention e Limiti
| Parametro | Valore | Note |
|---|---|---|
| Retention | 30 giorni | Configurabile in loki.yaml (retention_period) |
| Max query range | 7 giorni | Per query su intervalli piu ampi, segmentare |
| Max entries per query | 5000 | Incrementabile ma impatta le performance |
| Rate limit ingestion | 10 MB/s per tenant Loki | Non confondere con tenant POS |
| Max label name length | 1024 caratteri |
Query che scansionano grandi volumi di log (es. {namespace="pos-enterprise"} senza ulteriori filtri su 7 giorni) possono essere molto lente. Aggiungere sempre almeno un filtro su node_type o stream per restringere il risultato.
Comandi Utili
# Verificare che Promtail stia raccogliendo log
kubectl logs -n monitoring daemonset/promtail --tail=20
# Controllare i target di Promtail
kubectl port-forward -n monitoring daemonset/promtail 3101:3101
# Aprire: http://localhost:3101/targets
# Query Loki via CLI (logcli)
logcli query '{namespace="pos-enterprise"}' --limit=50 --addr=http://localhost:3100
# Verificare le label disponibili
logcli labels --addr=http://localhost:3100
logcli labels node_type --addr=http://localhost:3100
Integrazione Log-Tracce
Quando un log contiene un campo traceId, Grafana permette il passaggio diretto da Loki a Tempo:
- Nella vista Explore con Loki, espandere una riga di log
- Se presente il campo
traceId, apparira un link cliccabile - Il link apre la vista Tempo con lo span tree completo della richiesta
Questa integrazione e configurata nel data source Loki tramite Derived Fields:
# Configurazione Loki data source (Grafana provisioning)
derivedFields:
- name: traceId
matcherRegex: '"traceId":"([a-f0-9]+)"'
url: '$${__value.raw}'
datasourceUid: tempo
Riferimenti Incrociati
- Stack di Monitoring: Panoramica - Architettura Loki e Promtail
- Dashboard Grafana - Dashboard con pannelli log
- Troubleshooting Infrastrutturale - Pattern di log per problemi comuni
- Health Check e Probes - Log generati dal sistema di health check