Change:
When using or with a key/value that has a multi-line string as value, that value will appear as quoted string in the log message, i.e. the log message will still be a single line. It would be nicer to print the value in text mode so that it goes across multiple lines, with clear indicators for start and end of the value. Indention could be used to avoid mistaking some embedded line as the start of a new log message. In JSON mode, the value should be formatted normally. /sig instrumentation /wg structured-logging Always printing as a single line can can make the log output unreadable ().
When adding something like this, can be reverted and implemented differently.
My original thinking was that only objects that explicitly ask for multi-line output get formatted that way. But that means that we need to review log calls very carefully. An alternative is to automatically switch from quoting strings to multi-line output based on some heuristic, for example when there's more than one line break.
Here's a potential implementation:
If structured logging is needed in any other case I can contribute, please inform if needed.
/triage accepted /assign /priority important-soon /area logging /cc /cc
We have the same problem as in kube-scheduler also in kubelet, it's just that no-one has seen and/or complained about the multi-line strings. With the benchmark + log analysis tool from I found the following multi-line string parameters in a kubelet log: <details</details

Discussion:
SyncFrequency:      5 * time.Second, HTTPCheckFrequency: 5 * time.Second, } <del> go myKubelet.RunKubelet("", manifestUrl, servers[0], "localhost", 0) </del> <ins> go myKubelet.RunKubelet("", manifestUrl, servers[0], "localhost", "", 0) </ins> // Create a second kublet so that the guestbook example's two redis slaves both // have a place they can schedule.

SyncFrequency:      5 * time.Second, HTTPCheckFrequency: 5 * time.Second, } <del> go otherKubelet.RunKubelet("", "", servers[0], "localhost", 0) </del> <ins> go otherKubelet.RunKubelet("", "", servers[0], "localhost", "", 0) </ins> // Ok. we're good to go. glog.Infof("API Server started on %s", apiserver.URL)

import ( "flag" "math/rand" <ins> "os" </ins> "os/exec" "time"

address            = flag.String("address", "127.0.0.1", "The address for the info server to serve on") port               = flag.Uint("port", 10250, "The port for the info server to serve on") hostnameOverride   = flag.String("hostname_override", "", "If non-empty, will use this string as identification instead of the actual hostname.") <ins> dockerEndpoint     = flag.String("docker_endpoint", "", "If non-empty, use this for the docker endpoint to communicate with") </ins> ) const dockerBinary = "/usr/bin/docker"

// Set up logger for etcd client etcd.SetLogger(util.NewLogger("etcd ")) <del> endpoint := "unix:///var/run/docker.sock" </del> <ins> var endpoint string if len(*dockerEndpoint) > 0 { endpoint = *dockerEndpoint } else if len(os.Getenv("DOCKER_HOST")) > 0 { endpoint = os.Getenv("DOCKER_HOST") } else { endpoint = "unix:///var/run/docker.sock" } glog.Infof("Connecting to docker on %s", endpoint) </ins> dockerClient, err := docker.NewClient(endpoint) if err != nil { glog.Fatal("Couldn't connnect to docker.")

SyncFrequency:      *syncFrequency, HTTPCheckFrequency: *httpCheckFrequency, } <del> my_kubelet.RunKubelet(*config, *manifestUrl, *etcdServers, *address, *port) </del> <ins> my_kubelet.RunKubelet(*config, *manifestUrl, *etcdServers, *address, *dockerEndpoint, *port) </ins> }

// Starts background goroutines. If config_path, manifest_url, or address are empty, // they are not watched. Never returns. <del> func (kl *Kubelet) RunKubelet(config_path, manifest_url, etcd_servers, address string, port uint) { </del> <ins> func (kl *Kubelet) RunKubelet(config_path, manifest_url, etcd_servers, address, endpoint string, port uint) { </ins> if kl.DockerPuller == nil { <del> kl.DockerPuller = MakeDockerPuller() </del> <ins> kl.DockerPuller = MakeDockerPuller(endpoint) </ins> } updateChannel := make(chan manifestUpdate) if config_path != "" {

return "", nil } <del> type dockerPuller struct{} </del> <ins> type dockerPuller struct { endpoint string } </ins> <del> func MakeDockerPuller() DockerPuller { return dockerPuller{} </del> <ins> func MakeDockerPuller(endpoint string) DockerPuller { return dockerPuller{ endpoint: endpoint, } </ins> } <del> func (dockerPuller) Pull(image string) error { cmd := exec.Command("docker", "pull", image) </del> <ins> func (p dockerPuller) Pull(image string) error { var cmd *exec.Cmd if len(p.endpoint) == 0 { cmd = exec.Command("docker", "pull", image) } else { cmd = exec.Command("docker", "-H", p.endpoint, "pull", image) } </ins> err := cmd.Start() if err != nil { return err