Merge pull request #21889 from cpuguy83/logscmd_add_attrs
Add support for reading logs extra attrs
This commit is contained in:
commit
08ec3606f1
12 changed files with 74 additions and 11 deletions
api
daemon
docs/reference
integration-cli
man
pkg/jsonlog
|
@ -25,6 +25,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|||
follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
|
||||
since := cmd.String([]string{"-since"}, "", "Show logs since timestamp")
|
||||
times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
|
||||
details := cmd.Bool([]string{"-details"}, false, "Show extra details provided to logs")
|
||||
tail := cmd.String([]string{"-tail"}, "all", "Number of lines to show from the end of the logs")
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
|
@ -48,6 +49,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|||
Timestamps: *times,
|
||||
Follow: *follow,
|
||||
Tail: *tail,
|
||||
Details: *details,
|
||||
}
|
||||
responseBody, err := cli.client.ContainerLogs(context.Background(), name, options)
|
||||
if err != nil {
|
||||
|
|
|
@ -99,6 +99,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
|
|||
Tail: r.Form.Get("tail"),
|
||||
ShowStdout: stdout,
|
||||
ShowStderr: stderr,
|
||||
Details: httputils.BoolValue(r, "details"),
|
||||
},
|
||||
OutStream: w,
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, erro
|
|||
Source: l.Stream,
|
||||
Timestamp: l.Created,
|
||||
Line: []byte(l.Log),
|
||||
Attrs: l.Attrs,
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ package logger
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/jsonlog"
|
||||
|
@ -29,6 +31,31 @@ type Message struct {
|
|||
Line []byte
|
||||
Source string
|
||||
Timestamp time.Time
|
||||
Attrs LogAttributes
|
||||
}
|
||||
|
||||
// LogAttributes is used to hold the extra attributes available in the log message
|
||||
// Primarily used for converting the map type to string and sorting.
|
||||
type LogAttributes map[string]string
|
||||
type byKey []string
|
||||
|
||||
func (s byKey) Len() int { return len(s) }
|
||||
func (s byKey) Less(i, j int) bool {
|
||||
keyI := strings.Split(s[i], "=")
|
||||
keyJ := strings.Split(s[j], "=")
|
||||
return keyI[0] < keyJ[0]
|
||||
}
|
||||
func (s byKey) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (a LogAttributes) String() string {
|
||||
var ss byKey
|
||||
for k, v := range a {
|
||||
ss = append(ss, k+"="+v)
|
||||
}
|
||||
sort.Sort(ss)
|
||||
return strings.Join(ss, ",")
|
||||
}
|
||||
|
||||
// Logger is the interface for docker logging drivers.
|
||||
|
|
|
@ -90,6 +90,9 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c
|
|||
return nil
|
||||
}
|
||||
logLine := msg.Line
|
||||
if config.Details {
|
||||
logLine = append([]byte(msg.Attrs.String()+" "), logLine...)
|
||||
}
|
||||
if config.Timestamps {
|
||||
logLine = append([]byte(msg.Timestamp.Format(logger.TimeFormat)+" "), logLine...)
|
||||
}
|
||||
|
|
|
@ -136,6 +136,7 @@ This section lists each version from latest to oldest. Each listing includes a
|
|||
* `POST /auth` now returns an `IdentityToken` when supported by a registry.
|
||||
* `POST /containers/create` with both `Hostname` and `Domainname` fields specified will result in the container's hostname being set to `Hostname`, rather than `Hostname.Domainname`.
|
||||
* `GET /volumes` now supports more filters, new added filters are `name` and `driver`.
|
||||
* `GET /containers/(id or name)/logs` now accepts a `details` query parameter to stream the extra attributes that were provided to the containers `LogOpts`, such as environment variables and labels, with the logs.
|
||||
|
||||
### v1.22 API changes
|
||||
|
||||
|
|
|
@ -770,6 +770,7 @@ Get `stdout` and `stderr` logs from the container ``id``
|
|||
|
||||
Query Parameters:
|
||||
|
||||
- **details** - 1/True/true or 0/False/flase, Show extra details provided to logs. Default `false`.
|
||||
- **follow** – 1/True/true or 0/False/false, return stream. Default `false`.
|
||||
- **stdout** – 1/True/true or 0/False/false, show `stdout` log. Default `false`.
|
||||
- **stderr** – 1/True/true or 0/False/false, show `stderr` log. Default `false`.
|
||||
|
|
|
@ -14,6 +14,7 @@ parent = "smn_cli"
|
|||
|
||||
Fetch the logs of a container
|
||||
|
||||
--details Show extra details provided to logs
|
||||
-f, --follow Follow log output
|
||||
--help Print usage
|
||||
--since="" Show logs since timestamp
|
||||
|
@ -36,6 +37,10 @@ The `docker logs --timestamps` command will add an [RFC3339Nano timestamp](https
|
|||
log entry. To ensure that the timestamps are aligned the
|
||||
nano-second part of the timestamp will be padded with zero when necessary.
|
||||
|
||||
The `docker logs --details` command will add on extra attributes, such as
|
||||
environment variables and labels, provided to `--log-opt` when creating the
|
||||
container.
|
||||
|
||||
The `--since` option shows only the container logs generated after
|
||||
a given date. You can specify the date as an RFC 3339 date, a UNIX
|
||||
timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Besides RFC3339 date
|
||||
|
|
|
@ -307,3 +307,16 @@ func (s *DockerSuite) TestLogsCLIContainerNotFound(c *check.C) {
|
|||
message := fmt.Sprintf("Error: No such container: %s\n", name)
|
||||
c.Assert(out, checker.Equals, message)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestLogsWithDetails(c *check.C) {
|
||||
dockerCmd(c, "run", "--name=test", "--label", "foo=bar", "-e", "baz=qux", "--log-opt", "labels=foo", "--log-opt", "env=baz", "busybox", "echo", "hello")
|
||||
out, _ := dockerCmd(c, "logs", "--details", "--timestamps", "test")
|
||||
|
||||
logFields := strings.Fields(strings.TrimSpace(out))
|
||||
c.Assert(len(logFields), checker.Equals, 3, check.Commentf(out))
|
||||
|
||||
details := strings.Split(logFields[1], ",")
|
||||
c.Assert(details, checker.HasLen, 2)
|
||||
c.Assert(details[0], checker.Equals, "baz=qux")
|
||||
c.Assert(details[1], checker.Equals, "foo=bar")
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ logging drivers.
|
|||
**--help**
|
||||
Print usage statement
|
||||
|
||||
**--details**=*true*|*false*
|
||||
Show extra details provided to logs
|
||||
|
||||
**-f**, **--follow**=*true*|*false*
|
||||
Follow log output. The default is *false*.
|
||||
|
||||
|
@ -55,6 +58,10 @@ epoch or Unix time), and the optional .nanoseconds field is a fraction of a
|
|||
second no more than nine digits long. You can combine the `--since` option with
|
||||
either or both of the `--follow` or `--tail` options.
|
||||
|
||||
The `docker logs --details` command will add on extra attributes, such as
|
||||
environment variables and labels, provided to `--log-opt` when creating the
|
||||
container.
|
||||
|
||||
# HISTORY
|
||||
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
|
||||
based on docker.com source material and internal work.
|
||||
|
|
|
@ -15,6 +15,8 @@ type JSONLog struct {
|
|||
Stream string `json:"stream,omitempty"`
|
||||
// Created is the created timestamp of log
|
||||
Created time.Time `json:"time"`
|
||||
// Attrs is the list of extra attributes provided by the user
|
||||
Attrs map[string]string `json:"attrs,omitempty"`
|
||||
}
|
||||
|
||||
// Format returns the log formatted according to format
|
||||
|
|
|
@ -6,18 +6,18 @@ import (
|
|||
)
|
||||
|
||||
func TestJSONLogMarshalJSON(t *testing.T) {
|
||||
logs := map[JSONLog]string{
|
||||
JSONLog{Log: `"A log line with \\"`}: `^{\"log\":\"\\\"A log line with \\\\\\\\\\\"\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{Log: "A log line"}: `^{\"log\":\"A log line\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{Log: "A log line with \r"}: `^{\"log\":\"A log line with \\r\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{Log: "A log line with & < >"}: `^{\"log\":\"A log line with \\u0026 \\u003c \\u003e\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{Log: "A log line with utf8 : 🚀 ψ ω β"}: `^{\"log\":\"A log line with utf8 : 🚀 ψ ω β\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{Stream: "stdout"}: `^{\"stream\":\"stdout\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{}: `^{\"time\":\".{20,}\"}$`,
|
||||
logs := map[*JSONLog]string{
|
||||
&JSONLog{Log: `"A log line with \\"`}: `^{\"log\":\"\\\"A log line with \\\\\\\\\\\"\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Log: "A log line"}: `^{\"log\":\"A log line\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Log: "A log line with \r"}: `^{\"log\":\"A log line with \\r\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Log: "A log line with & < >"}: `^{\"log\":\"A log line with \\u0026 \\u003c \\u003e\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Log: "A log line with utf8 : 🚀 ψ ω β"}: `^{\"log\":\"A log line with utf8 : 🚀 ψ ω β\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Stream: "stdout"}: `^{\"stream\":\"stdout\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{}: `^{\"time\":\".{20,}\"}$`,
|
||||
// These ones are a little weird
|
||||
JSONLog{Log: "\u2028 \u2029"}: `^{\"log\":\"\\u2028 \\u2029\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{Log: string([]byte{0xaF})}: `^{\"log\":\"\\ufffd\",\"time\":\".{20,}\"}$`,
|
||||
JSONLog{Log: string([]byte{0x7F})}: `^{\"log\":\"\x7f\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Log: "\u2028 \u2029"}: `^{\"log\":\"\\u2028 \\u2029\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Log: string([]byte{0xaF})}: `^{\"log\":\"\\ufffd\",\"time\":\".{20,}\"}$`,
|
||||
&JSONLog{Log: string([]byte{0x7F})}: `^{\"log\":\"\x7f\",\"time\":\".{20,}\"}$`,
|
||||
}
|
||||
for jsonLog, expression := range logs {
|
||||
data, err := jsonLog.MarshalJSON()
|
||||
|
|
Loading…
Add table
Reference in a new issue