From 87a450a37f0e934b67c90fbcdbff28459216e332 Mon Sep 17 00:00:00 2001
From: Vincent Woo <me@vincentwoo.com>
Date: Sun, 13 Dec 2015 02:10:41 -0800
Subject: [PATCH 1/2] Allow disabling of colored Docker logs via daemon flag.

Signed-off-by: Vincent Woo <me@vincentwoo.com>
Signed-off-by: David Calavera <david.calavera@gmail.com>
---
 contrib/completion/bash/docker       |  1 +
 contrib/completion/zsh/_docker       |  1 +
 contrib/init/upstart/docker.conf     |  2 +-
 daemon/config.go                     |  2 ++
 docker/daemon.go                     |  5 ++++-
 docs/reference/commandline/daemon.md | 28 +++++++++++++++-------------
 man/docker-daemon.8.md               |  6 ++++++
 7 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker
index 8fb31c2288..ba74289a79 100644
--- a/contrib/completion/bash/docker
+++ b/contrib/completion/bash/docker
@@ -748,6 +748,7 @@ _docker_daemon() {
 		--ip-masq=false
 		--iptables=false
 		--ipv6
+		--raw-logs
 		--selinux-enabled
 		--userland-proxy=false
 	"
diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker
index 25be25e02d..b77435a156 100644
--- a/contrib/completion/zsh/_docker
+++ b/contrib/completion/zsh/_docker
@@ -666,6 +666,7 @@ __docker_subcommand() {
                 "($help)*--log-opt=[Log driver specific options]:log driver options:__docker_log_options" \
                 "($help)--mtu=[Set the containers network MTU]:mtu:(0 576 1420 1500 9000)" \
                 "($help -p --pidfile)"{-p=,--pidfile=}"[Path to use for daemon PID file]:PID file:_files" \
+                "($help)--raw-logs[Full timestamps without ANSI coloring]" \
                 "($help)*--registry-mirror=[Preferred Docker registry mirror]:registry mirror: " \
                 "($help -s --storage-driver)"{-s=,--storage-driver=}"[Storage driver to use]:driver:(aufs devicemapper btrfs zfs overlay)" \
                 "($help)--selinux-enabled[Enable selinux support]" \
diff --git a/contrib/init/upstart/docker.conf b/contrib/init/upstart/docker.conf
index b40340847e..72fa69b73b 100644
--- a/contrib/init/upstart/docker.conf
+++ b/contrib/init/upstart/docker.conf
@@ -39,7 +39,7 @@ script
 	if [ -f /etc/default/$UPSTART_JOB ]; then
 		. /etc/default/$UPSTART_JOB
 	fi
-	exec "$DOCKER" daemon $DOCKER_OPTS
+	exec "$DOCKER" daemon $DOCKER_OPTS --raw-logs
 end script
 
 # Don't emit "started" event until docker.sock is ready.
diff --git a/daemon/config.go b/daemon/config.go
index 932d535e57..e0b07b056d 100644
--- a/daemon/config.go
+++ b/daemon/config.go
@@ -57,6 +57,7 @@ type CommonConfig struct {
 	Labels               []string            `json:"labels,omitempty"`
 	Mtu                  int                 `json:"mtu,omitempty"`
 	Pidfile              string              `json:"pidfile,omitempty"`
+	RawLogs              bool                `json:"raw-logs,omitempty"`
 	Root                 string              `json:"graph,omitempty"`
 	TrustKeyPath         string              `json:"-"`
 
@@ -104,6 +105,7 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string)
 	cmd.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, usageFn("--restart on the daemon has been deprecated in favor of --restart policies on docker run"))
 	cmd.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", usageFn("Storage driver to use"))
 	cmd.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, usageFn("Set the containers network MTU"))
+	cmd.BoolVar(&config.RawLogs, []string{"-raw-logs"}, false, usageFn("Full timestamps without ANSI coloring"))
 	// FIXME: why the inconsistency between "hosts" and "sockets"?
 	cmd.Var(opts.NewListOptsRef(&config.DNS, opts.ValidateIPAddress), []string{"#dns", "-dns"}, usageFn("DNS server to use"))
 	cmd.Var(opts.NewNamedListOptsRef("dns-opts", &config.DNSOptions, nil), []string{"-dns-opt"}, usageFn("DNS options to use"))
diff --git a/docker/daemon.go b/docker/daemon.go
index 0f85419215..793fd56cc6 100644
--- a/docker/daemon.go
+++ b/docker/daemon.go
@@ -168,7 +168,10 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 		logrus.Warn("Running experimental build")
 	}
 
-	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: jsonlog.RFC3339NanoFixed})
+	logrus.SetFormatter(&logrus.TextFormatter{
+		TimestampFormat: jsonlog.RFC3339NanoFixed,
+		DisableColors:   cli.Config.RawLogs,
+	})
 
 	if err := setDefaultUmask(); err != nil {
 		logrus.Fatalf("Failed to set umask: %v", err)
diff --git a/docs/reference/commandline/daemon.md b/docs/reference/commandline/daemon.md
index df823995cd..1986295414 100644
--- a/docs/reference/commandline/daemon.md
+++ b/docs/reference/commandline/daemon.md
@@ -54,6 +54,7 @@ weight = -1
       --mtu=0                                Set the containers network MTU
       --disable-legacy-registry              Do not contact legacy registries
       -p, --pidfile="/var/run/docker.pid"    Path to use for daemon PID file
+      --raw-logs                             Full timestamps without ANSI coloring
       --registry-mirror=[]                   Preferred Docker registry mirror
       -s, --storage-driver=""                Storage driver to use
       --selinux-enabled                      Enable selinux support
@@ -860,19 +861,20 @@ This is a full example of the allowed configuration options in the file:
 	"group": "",
 	"cgroup-parent": "",
 	"default-ulimits": {},
-       "ipv6": false,
-       "iptables": false,
-       "ip-forward": false,
-       "ip-mask": false,
-       "userland-proxy": false,
-       "ip": "0.0.0.0",
-       "bridge": "",
-       "bip": "",
-       "fixed-cidr": "",
-       "fixed-cidr-v6": "",
-       "default-gateway": "",
-       "default-gateway-v6": "",
-       "icc": false
+	"ipv6": false,
+	"iptables": false,
+	"ip-forward": false,
+	"ip-mask": false,
+	"userland-proxy": false,
+	"ip": "0.0.0.0",
+	"bridge": "",
+	"bip": "",
+	"fixed-cidr": "",
+	"fixed-cidr-v6": "",
+	"default-gateway": "",
+	"default-gateway-v6": "",
+	"icc": false,
+	"raw-logs": false
 }
 ```
 
diff --git a/man/docker-daemon.8.md b/man/docker-daemon.8.md
index 02adaeda91..051c9e0748 100644
--- a/man/docker-daemon.8.md
+++ b/man/docker-daemon.8.md
@@ -44,6 +44,7 @@ docker-daemon - Enable daemon mode
 [**--log-opt**[=*map[]*]]
 [**--mtu**[=*0*]]
 [**-p**|**--pidfile**[=*/var/run/docker.pid*]]
+[**--raw-logs**]
 [**--registry-mirror**[=*[]*]]
 [**-s**|**--storage-driver**[=*STORAGE-DRIVER*]]
 [**--selinux-enabled**]
@@ -197,6 +198,11 @@ unix://[/path/to/socket] to use.
 **-p**, **--pidfile**=""
   Path to use for daemon PID file. Default is `/var/run/docker.pid`
 
+**--raw-logs**
+Output daemon logs in full timestamp format without ANSI coloring. If this flag is not set,
+the daemon outputs condensed, colorized logs if a terminal is detected, or full ("raw")
+output otherwise.
+
 **--registry-mirror**=*<scheme>://<host>*
   Prepend a registry mirror to be used for image pulls. May be specified multiple times.
 

From 898599171e2f36a1cf36a2eea31d82a226acd802 Mon Sep 17 00:00:00 2001
From: David Calavera <david.calavera@gmail.com>
Date: Thu, 28 Jan 2016 13:33:35 -0500
Subject: [PATCH 2/2] Add test to make sure raw logs are properly activated.

Signed-off-by: David Calavera <david.calavera@gmail.com>
---
 integration-cli/docker_cli_daemon_test.go     | 41 ++++++++++++++++---
 ...cker_cli_external_graphdriver_unix_test.go |  2 +-
 integration-cli/docker_utils.go               | 26 +++++++-----
 3 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go
index 8faf0719be..a9cf86d90a 100644
--- a/integration-cli/docker_cli_daemon_test.go
+++ b/integration-cli/docker_cli_daemon_test.go
@@ -6,6 +6,7 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"net"
 	"os"
@@ -22,6 +23,7 @@ import (
 	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libtrust"
 	"github.com/go-check/check"
+	"github.com/kr/pty"
 )
 
 func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *check.C) {
@@ -584,7 +586,7 @@ func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *check.C) {
 			c.Fatalf("Expected daemon not to start, got %v", err)
 		}
 		// look in the log and make sure we got the message that daemon is shutting down
-		runCmd := exec.Command("grep", "Error starting daemon", s.d.LogfileName())
+		runCmd := exec.Command("grep", "Error starting daemon", s.d.LogFileName())
 		if out, _, err := runCommandWithOutput(runCmd); err != nil {
 			c.Fatalf("Expected 'Error starting daemon' message; but doesn't exist in log: %q, err: %v", out, err)
 		}
@@ -1759,7 +1761,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartLocalVolumes(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonCorruptedLogDriverAddress(c *check.C) {
 	c.Assert(s.d.Start("--log-driver=syslog", "--log-opt", "syslog-address=corrupted:42"), check.NotNil)
 	expected := "Failed to set log opts: syslog-address should be in form proto://address"
-	runCmd := exec.Command("grep", expected, s.d.LogfileName())
+	runCmd := exec.Command("grep", expected, s.d.LogFileName())
 	if out, _, err := runCommandWithOutput(runCmd); err != nil {
 		c.Fatalf("Expected %q message; but doesn't exist in log: %q, err: %v", expected, out, err)
 	}
@@ -1768,7 +1770,7 @@ func (s *DockerDaemonSuite) TestDaemonCorruptedLogDriverAddress(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonCorruptedFluentdAddress(c *check.C) {
 	c.Assert(s.d.Start("--log-driver=fluentd", "--log-opt", "fluentd-address=corrupted:c"), check.NotNil)
 	expected := "Failed to set log opts: invalid fluentd-address corrupted:c: "
-	runCmd := exec.Command("grep", expected, s.d.LogfileName())
+	runCmd := exec.Command("grep", expected, s.d.LogFileName())
 	if out, _, err := runCommandWithOutput(runCmd); err != nil {
 		c.Fatalf("Expected %q message; but doesn't exist in log: %q, err: %v", expected, out, err)
 	}
@@ -1922,7 +1924,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *check.C) {
 	c.Assert(err, check.IsNil)
 	// clear the log file -- we don't need any of it but may for the next part
 	// can ignore the error here, this is just a cleanup
-	os.Truncate(d.LogfileName(), 0)
+	os.Truncate(d.LogFileName(), 0)
 	err = d.Start()
 	c.Assert(err, check.IsNil)
 
@@ -1930,7 +1932,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *check.C) {
 		out, err := d.Cmd("inspect", "-f", "{{ .State.Running }}", "parent"+num)
 		c.Assert(err, check.IsNil)
 		if strings.TrimSpace(out) != "true" {
-			log, _ := ioutil.ReadFile(d.LogfileName())
+			log, _ := ioutil.ReadFile(d.LogFileName())
 			c.Fatalf("parent container is not running\n%s", string(log))
 		}
 	}
@@ -2064,3 +2066,32 @@ func (s *DockerDaemonSuite) TestRunLinksChanged(c *check.C) {
 	c.Assert(err, check.NotNil, check.Commentf(out))
 	c.Assert(out, check.Not(checker.Contains), "1 packets transmitted, 1 packets received")
 }
+
+func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	newD := NewDaemon(c)
+
+	infoLog := "\x1b[34mINFO\x1b"
+
+	p, tty, err := pty.Open()
+	c.Assert(err, checker.IsNil)
+	defer func() {
+		tty.Close()
+		p.Close()
+	}()
+
+	b := bytes.NewBuffer(nil)
+	go io.Copy(b, p)
+
+	// Enable coloring explicitly
+	newD.StartWithLogFile(tty, "--raw-logs=false")
+	newD.Stop()
+	c.Assert(b.String(), checker.Contains, infoLog)
+
+	b.Reset()
+
+	// Disable coloring explicitly
+	newD.StartWithLogFile(tty, "--raw-logs=true")
+	newD.Stop()
+	c.Assert(b.String(), check.Not(checker.Contains), infoLog)
+}
diff --git a/integration-cli/docker_cli_external_graphdriver_unix_test.go b/integration-cli/docker_cli_external_graphdriver_unix_test.go
index 3dcbe620ab..9fd36ec9c1 100644
--- a/integration-cli/docker_cli_external_graphdriver_unix_test.go
+++ b/integration-cli/docker_cli_external_graphdriver_unix_test.go
@@ -299,7 +299,7 @@ func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
 
 func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
 	if err := s.d.StartWithBusybox("-s", "test-external-graph-driver"); err != nil {
-		b, _ := ioutil.ReadFile(s.d.LogfileName())
+		b, _ := ioutil.ReadFile(s.d.LogFileName())
 		c.Assert(err, check.IsNil, check.Commentf("\n%s", string(b)))
 	}
 
diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go
index 12bb252839..04bb5b4079 100644
--- a/integration-cli/docker_utils.go
+++ b/integration-cli/docker_utils.go
@@ -205,7 +205,15 @@ func (d *Daemon) getClientConfig() (*clientConfig, error) {
 
 // Start will start the daemon and return once it is ready to receive requests.
 // You can specify additional daemon flags.
-func (d *Daemon) Start(arg ...string) error {
+func (d *Daemon) Start(args ...string) error {
+	logFile, err := os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
+	d.c.Assert(err, check.IsNil, check.Commentf("[%s] Could not create %s/docker.log", d.id, d.folder))
+
+	return d.StartWithLogFile(logFile, args...)
+}
+
+// StartWithLogFile will start the daemon and attach its streams to a given file.
+func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
 	dockerBinary, err := exec.LookPath(dockerBinary)
 	d.c.Assert(err, check.IsNil, check.Commentf("[%s] could not find docker binary in $PATH", d.id))
 
@@ -226,7 +234,7 @@ func (d *Daemon) Start(arg ...string) error {
 	// turn on debug mode
 	foundLog := false
 	foundSd := false
-	for _, a := range arg {
+	for _, a := range providedArgs {
 		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
 			foundLog = true
 		}
@@ -241,14 +249,12 @@ func (d *Daemon) Start(arg ...string) error {
 		args = append(args, "--storage-driver", d.storageDriver)
 	}
 
-	args = append(args, arg...)
+	args = append(args, providedArgs...)
 	d.cmd = exec.Command(dockerBinary, args...)
 
-	d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
-	d.c.Assert(err, check.IsNil, check.Commentf("[%s] Could not create %s/docker.log", d.id, d.folder))
-
-	d.cmd.Stdout = d.logFile
-	d.cmd.Stderr = d.logFile
+	d.cmd.Stdout = out
+	d.cmd.Stderr = out
+	d.logFile = out
 
 	if err := d.cmd.Start(); err != nil {
 		return fmt.Errorf("[%s] could not start daemon container: %v", d.id, err)
@@ -472,8 +478,8 @@ func (d *Daemon) CmdWithArgs(daemonArgs []string, name string, arg ...string) (s
 	return string(b), err
 }
 
-// LogfileName returns the path the the daemon's log file
-func (d *Daemon) LogfileName() string {
+// LogFileName returns the path the the daemon's log file
+func (d *Daemon) LogFileName() string {
 	return d.logFile.Name()
 }