Skip to main content

Analisi Log con Loki

Accesso a Loki via Grafana Explore

Loki non ha una propria UI: si accede esclusivamente tramite Grafana Explore.

  1. Aprire Grafana (https://grafana.platform.sellogic.cloud)
  2. Navigare a Explore (icona bussola nella sidebar)
  3. Selezionare la data source Loki dal dropdown in alto
  4. Inserire la query LogQL nella barra di ricerca
Shortcut

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-enterprise
  • pod: nome del pod Kubernetes
  • container: nome del container
  • node_type: tipo di nodo Moleculer (es. core, gateway, commerce)
  • stream: stdout o stderr

Query LogQL: Fondamentali

Sintassi Base

{label="valore"} |= "testo da cercare"
  • Stream selector {...}: filtra per label (obbligatorio)
  • Pipeline: |= (contiene), != (non contiene), |~ (regex), !~ (non regex)
  • Parser: | json per 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

ParametroValoreNote
Retention30 giorniConfigurabile in loki.yaml (retention_period)
Max query range7 giorniPer query su intervalli piu ampi, segmentare
Max entries per query5000Incrementabile ma impatta le performance
Rate limit ingestion10 MB/s per tenant LokiNon confondere con tenant POS
Max label name length1024 caratteri
Query Pesanti

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:

  1. Nella vista Explore con Loki, espandere una riga di log
  2. Se presente il campo traceId, apparira un link cliccabile
  3. 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

Questa pagina ti è stata utile?