Files
fn_registry/functions/infra/push_loki_stream.md
T

93 lines
4.0 KiB
Markdown

---
name: push_loki_stream
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func PushLokiStream(endpoint string, user string, pass string, labels map[string]string, timestampsNs []int64, lines []string) error"
description: "Envia lineas de log a un servidor Grafana Loki via su push API. Construye el cuerpo JSON {\"streams\":[{\"stream\":{labels},\"values\":[[\"<ts_ns>\",\"<line>\"],...]}]} y lo POSTea al endpoint. Soporta Basic Auth opcional, valida que timestamps y lineas tengan igual longitud, es no-op si no hay lineas, y exige status 2xx. Solo stdlib, TLS verificado."
tags: [loki, grafana, logs, push, metrics, http, json, stdlib, infra, fleet-metrics]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: ["bytes", "encoding/json", "fmt", "io", "net/http", "strconv", "time"]
params:
- name: endpoint
desc: "URL completa del push API de Loki (ej https://logs-xxxx.organic-machine.com/loki/api/v1/push)"
- name: user
desc: "usuario para Basic Auth; si es cadena vacia no se envia Authorization"
- name: pass
desc: "password para Basic Auth; solo se usa cuando user != ''"
- name: labels
desc: "labels del stream Loki (ej {instance:lucas, job:journald, unit:ssh.service}); van tal cual en el campo stream"
- name: timestampsNs
desc: "timestamps en nanosegundos desde epoch, uno por linea; debe tener la misma longitud que lines"
- name: lines
desc: "lineas de log a enviar, alineadas posicionalmente con timestampsNs; si esta vacio la funcion es no-op"
output: "error si la peticion falla, las longitudes no coinciden o el status no es 2xx; nil en exito (incluido el no-op de 0 lineas)"
tested: true
tests:
- "JSON enviado tiene estructura streams/stream/values correcta"
- "longitudes desiguales dan error antes del POST"
- "len lines cero es no-op sin peticion"
- "Basic Auth presente cuando user no vacio"
- "status 500 produce error"
test_file_path: "functions/infra/push_loki_stream_test.go"
file_path: "functions/infra/push_loki_stream.go"
---
## Ejemplo
```go
labels := map[string]string{
"instance": "lucas",
"job": "journald",
"unit": "ssh.service",
}
nowNs := time.Now().UnixNano()
ts := []int64{nowNs, nowNs + 1}
lines := []string{
"Accepted publickey for lucas from 10.0.0.2",
"session opened for user lucas",
}
err := PushLokiStream(
"https://logs-abcd.organic-machine.com/loki/api/v1/push",
"tenant1", // user para Basic Auth (vacio = sin auth)
"s3cr3t", // pass
labels,
ts,
lines,
)
if err != nil {
return err
}
```
## Cuando usarla
Cuando necesites enviar lineas de log a Grafana Loki desde un agente o servicio Go
(ej. reenviar journald, eventos de una app, o lineas de un tailer) sin arrastrar el
cliente oficial de Loki. Util para alimentar dashboards de la flota (`fleet-metrics`)
con logs etiquetados por instancia/job/unit. Pasa los logs ya batcheados: un solo
stream por llamada con sus labels.
## Gotchas
- `timestampsNs` y `lines` deben tener exactamente la misma longitud; si no, retorna
error ANTES de hacer la peticion (no envia nada).
- `len(lines)==0` es un no-op deliberado: retorna `nil` sin tocar la red. Comprueba el
caso vacio en el caller si necesitas distinguir "no habia logs" de "envio ok".
- Loki exige timestamps en NANOSEGUNDOS. Pasar segundos o milisegundos hace que las
lineas caigan fuera de la ventana de retencion y Loki las rechace silenciosamente.
- Dentro de un mismo stream las entradas deberian ir en orden creciente de timestamp;
Loki puede rechazar entradas fuera de orden segun su config.
- Exito = status 2xx (Loki normalmente devuelve 204 No Content). Un 4xx/5xx produce
error con el codigo + primeros 200 bytes del cuerpo de respuesta para diagnostico.
- TLS verificado (sin InsecureSkipVerify) y `http.Client` con timeout de 10s fijo. Para
endpoints con certificado interno hace falta CA confiable en el sistema.
- Secretos (`user`/`pass`): nunca hardcodear — resolver desde `pass`/vault en el caller.