Log Drains ​
This guide documents AYB's shipped external log-drain system: HTTP/Datadog/Loki drain types, worker batching/retry behavior, admin runtime APIs, and config defaults.
Source of truth:
internal/server/admin_drains_handler.gointernal/server/server_init.go::normalizeLogDrainConfiginternal/logging/drain.gointernal/logging/http_drain.gointernal/logging/datadog_drain.gointernal/logging/loki_drain.gointernal/logging/worker.gointernal/logging/manager.go- Tests:
internal/server/admin_drains_handler_test.go,internal/logging/drain_test.go
Supported drain types ​
Only these drain type values are implemented:
httpdatadogloki
Any other type is rejected.
Config format ​
Each [[logging.drains]] entry maps to one LogDrainConfig:
idtypeurlheadersbatch_sizeflush_interval_secondsenabled
Defaults (from validation/normalization):
id:drain-<index>(startup config) ordrain-<timestamp>(runtime create path)enabled:truebatch_size:100flush_interval_seconds:5headers:{}
Example:
[[logging.drains]]
id = "ops-http"
type = "http"
url = "https://logs.example.com/ingest"
enabled = true
batch_size = 200
flush_interval_seconds = 3
[logging.drains.headers]
Authorization = "Bearer token"Admin runtime API ​
All endpoints require admin auth (Authorization: Bearer <admin-token>).
GET /api/admin/logging/drainsPOST /api/admin/logging/drainsDELETE /api/admin/logging/drains/{id}
Create drain ​
curl -X POST http://localhost:8090/api/admin/logging/drains \
-H "Authorization: Bearer $AYB_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "runtime-loki",
"type": "loki",
"url": "https://loki.example.com/loki/api/v1/push",
"headers": {"Authorization": "Bearer loki-token"},
"batch_size": 100,
"flush_interval_seconds": 5,
"enabled": true
}'If enabled=false, AYB returns 201 but does not register a worker.
List drains response ​
GET /api/admin/logging/drains returns an array of:
idnamestats
stats fields:
sentfaileddropped
Delivery semantics ​
Each active drain runs in a DrainWorker:
- queue size:
10000(set by server wiring) - non-blocking enqueue fanout from
DrainManager - flush triggers:
- batch size reached
- flush interval elapsed
Retry behavior (sendWithRetry):
- default max retries: 3
- exponential backoff with +/-25% jitter
- default backoff: base 1s, cap 30s
- after retries are exhausted, entries are marked dropped via
DropReporter
Important nuance:
- queue overflow drops happen before send-retry and are non-blocking; they are not surfaced as request failures.
Payload formats ​
HTTP drain ​
POSTtourlContent-Type: application/json- body: JSON array of
LogEntry
LogEntry fields:
timestamplevelmessagesourcefields
Datadog drain ​
- JSON array of Datadog log objects
- includes:
ddsource: "ayb"service: "ayb"statusmessagetimestamp(Unix ms)attributes(from AYBfields)
Loki drain ​
POSTto Loki push endpoint (typically/loki/api/v1/push)- payload shape:
{ "streams": [...] } - entries grouped into streams by
level+source, with stream labels:source: "ayb"level- optional
log_source
- line payload is JSON including
message,level,log_source, and structured fields - if a custom field collides with reserved keys, Loki line writer prefixes it with
field_
Startup config vs runtime drains ​
- Startup: AYB loads
logging.drainsconfig, normalizes, and registers only enabled + valid drains. - Runtime admin create/delete: applies in memory via
DrainManageronly.
Disabled config entries are valid config, but they are not active runtime drains and therefore do not appear in list output.
Runtime-added drains are not persisted back into ayb.toml.