93 lines
4.0 KiB
Markdown
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.
|