Files
moby/daemon/server/httputils
Sebastiaan van Stijn d92a8c97de container/logs: add support for JSON streams (experimental)
This adds support for streaming logs as JSON stream either through an
"Accept" header ("application/jsonl", "application/x-ndjson", or
"application/json-seq"), or through a `format=json` query variable.

By default log messages are included as UTF-8 encoded strings, which should
work for regular logs, but can be lossy.

```bash
docker rm -fv my-container3
docker run \
    -qit \
    --name my-container3 \
    --label=labelA=foo \
    --label=labelB=bar \
    --log-opt labels=labelA,labelB \
    ubuntu bash
```

Inside the container:

```bash
root@b36bfcd439e9:/# echo {1..9} && exit
1 2 3 4 5 6 7 8 9
exit
```

With the message encoded as utf-8;

```bash
curl -s --unix-socket /var/run/docker.sock 'http://localhost/v1.54/containers/my-container3/logs?stdout=1&stderr=1&format=json' | jq .
{
  "Line": "\u001b[?2004h\u001b]0;root@1065b85dd167: /\u0007root@1065b85dd167:/# \u001b[7mecho {1..9} && exit\u001b[27m\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\becho {1..9} && exit\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.083531419Z"
}
{
  "Line": "\u001b[?2004l\r1 2 3 4 5 6 7 8 9\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.088152377Z"
}
{
  "Line": "exit\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.088183336Z"
}
```

With `details` enabled;

```bash
curl -s --unix-socket /var/run/docker.sock 'http://localhost/v1.54/containers/my-container3/logs?stdout=1&stderr=1&format=json&details=1' | jq .
{
  "Line": "\u001b[?2004h\u001b]0;root@1065b85dd167: /\u0007root@1065b85dd167:/# \u001b[7mecho {1..9} && exit\u001b[27m\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\becho {1..9} && exit\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.083531419Z",
  "Attrs": [{"Key": "labelA","Value": "foo"},{"Key": "labelB","Value": "bar"}]
}
{
  "Line": "\u001b[?2004l\r1 2 3 4 5 6 7 8 9\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.088152377Z",
  "Attrs": [{"Key": "labelA","Value": "foo"},{"Key": "labelB","Value": "bar"}]
}
{
  "Line": "exit\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.088183336Z",
  "Attrs": [{"Key": "labelA","Value": "foo"},{"Key": "labelB","Value": "bar"}]
}
```

Using `Accept` header instead of the `format=json` query parameter;

```bash
curl -s -H 'Accept: application/json-seq' --unix-socket /var/run/docker.sock 'http://localhost/v1.54/containers/my-container3/logs?stdout=1&stderr=1' | jq .
{
  "Line": "\u001b[?2004h\u001b]0;root@1065b85dd167: /\u0007root@1065b85dd167:/# \u001b[7mecho {1..9} && exit\u001b[27m\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\becho {1..9} && exit\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.083531419Z"
}
{
  "Line": "\u001b[?2004l\r1 2 3 4 5 6 7 8 9\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.088152377Z"
}
{
  "Line": "exit\r\n",
  "Source": "stdout",
  "Timestamp": "2026-02-20T15:39:01.088183336Z"
}
```

Metadata about partial messages is included (except for the json-file logging
driver, which doesn't use it currently);

The partial messages also don't get a trailing newline, except for the last entry;

```bash
docker run --name bar alpine sh -c 'head -c 20000 /dev/zero | tr "\0" A; echo'
curl -s --unix-socket /var/run/docker.sock 'http://localhost/v1.54/containers/bar/logs?stdout=1&stderr=1&format=json'
{"Line":"AAAAAAAAAA....AAAA","Source":"stdout","Timestamp":"2026-02-21T21:45:47.075093919Z"}
{"Line":"AAAAAAAAAAAAAAAA\n","Source":"stdout","Timestamp":"2026-02-21T21:45:47.075093919Z"}
```

And for logging drivers other than the `json-file`, we have information about
partials in a slightly more structured format:

```bash
docker run --name baz --log-driver=local alpine sh -c 'head -c 20000 /dev/zero | tr "\0" A; echo'
curl -s --unix-socket /var/run/docker.sock 'http://localhost/v1.54/containers/baz/logs?stdout=1&stderr=1&format=json'
{"Line":"AAAAAAAAAA....ASAA","Source":"stdout","Timestamp":"2026-02-21T21:49:42.453363584Z","LogMetaData":{"Last":false,"ID":"46a9188a552a068c1d63a5096b7335788aea7e430817f3d2d83c5fd90c6365ef","Ordinal":1}}
{"Line":"AAAAAAAAAAAAAAAA\n","Source":"stdout","Timestamp":"2026-02-21T21:49:42.453363584Z","LogMetaData":{"Last":true,"ID":"46a9188a552a068c1d63a5096b7335788aea7e430817f3d2d83c5fd90c6365ef","Ordinal":2}}
```

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 21:40:52 +02:00
..