Update dockerd to support JSON logging format
Update docker to support a '--log-format' option, which accepts either 'text' (default) or 'json'. Propagate the log format to containerd as well, to ensure that everything will be logged consistently. Signed-off-by: Philip K. Warren <pkwarren@gmail.com>
This commit is contained in:
parent
d2452c2102
commit
a08abec9f8
5 changed files with 71 additions and 5 deletions
|
@ -478,6 +478,7 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
|
||||||
conf.Debug = opts.Debug
|
conf.Debug = opts.Debug
|
||||||
conf.Hosts = opts.Hosts
|
conf.Hosts = opts.Hosts
|
||||||
conf.LogLevel = opts.LogLevel
|
conf.LogLevel = opts.LogLevel
|
||||||
|
conf.LogFormat = opts.LogFormat
|
||||||
|
|
||||||
if flags.Changed(FlagTLS) {
|
if flags.Changed(FlagTLS) {
|
||||||
conf.TLS = &opts.TLS
|
conf.TLS = &opts.TLS
|
||||||
|
@ -657,6 +658,10 @@ func (cli *DaemonCli) getContainerdDaemonOpts() ([]supervisor.DaemonOpt, error)
|
||||||
opts = append(opts, supervisor.WithLogLevel(cli.LogLevel))
|
opts = append(opts, supervisor.WithLogLevel(cli.LogLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if logFormat := cli.Config.LogFormat; logFormat != "" {
|
||||||
|
opts = append(opts, supervisor.WithLogFormat(logFormat))
|
||||||
|
}
|
||||||
|
|
||||||
if !cli.CriContainerd {
|
if !cli.CriContainerd {
|
||||||
// CRI support in the managed daemon is currently opt-in.
|
// CRI support in the managed daemon is currently opt-in.
|
||||||
//
|
//
|
||||||
|
@ -867,11 +872,26 @@ func configureDaemonLogs(conf *config.Config) {
|
||||||
} else {
|
} else {
|
||||||
logrus.SetLevel(logrus.InfoLevel)
|
logrus.SetLevel(logrus.InfoLevel)
|
||||||
}
|
}
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{
|
logFormat := conf.LogFormat
|
||||||
TimestampFormat: jsonmessage.RFC3339NanoFixed,
|
if logFormat == "" {
|
||||||
DisableColors: conf.RawLogs,
|
logFormat = log.TextFormat
|
||||||
FullTimestamp: true,
|
}
|
||||||
})
|
var formatter logrus.Formatter
|
||||||
|
switch logFormat {
|
||||||
|
case log.JSONFormat:
|
||||||
|
formatter = &logrus.JSONFormatter{
|
||||||
|
TimestampFormat: jsonmessage.RFC3339NanoFixed,
|
||||||
|
}
|
||||||
|
case log.TextFormat:
|
||||||
|
formatter = &logrus.TextFormatter{
|
||||||
|
TimestampFormat: jsonmessage.RFC3339NanoFixed,
|
||||||
|
DisableColors: conf.RawLogs,
|
||||||
|
FullTimestamp: true,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unsupported log format " + logFormat)
|
||||||
|
}
|
||||||
|
logrus.SetFormatter(formatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureProxyEnv(conf *config.Config) {
|
func configureProxyEnv(conf *config.Config) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/docker/docker/daemon/config"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -155,6 +156,26 @@ func TestLoadDaemonCliConfigWithLogLevel(t *testing.T) {
|
||||||
assert.Check(t, is.Equal("warn", loadedConfig.LogLevel))
|
assert.Check(t, is.Equal("warn", loadedConfig.LogLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadDaemonCliConfigWithLogFormat(t *testing.T) {
|
||||||
|
tempFile := fs.NewFile(t, "config", fs.WithContent(`{"log-format": "json"}`))
|
||||||
|
defer tempFile.Remove()
|
||||||
|
|
||||||
|
opts := defaultOptions(t, tempFile.Path())
|
||||||
|
loadedConfig, err := loadDaemonCliConfig(opts)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Assert(t, loadedConfig != nil)
|
||||||
|
assert.Check(t, is.Equal(log.JSONFormat, loadedConfig.LogFormat))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadDaemonCliConfigWithInvalidLogFormat(t *testing.T) {
|
||||||
|
tempFile := fs.NewFile(t, "config", fs.WithContent(`{"log-format": "foo"}`))
|
||||||
|
defer tempFile.Remove()
|
||||||
|
|
||||||
|
opts := defaultOptions(t, tempFile.Path())
|
||||||
|
_, err := loadDaemonCliConfig(opts)
|
||||||
|
assert.Check(t, is.ErrorContains(err, "invalid log format: foo"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) {
|
func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) {
|
||||||
content := `{"tlscacert": "/etc/certs/ca.pem", "log-driver": "syslog"}`
|
content := `{"tlscacert": "/etc/certs/ca.pem", "log-driver": "syslog"}`
|
||||||
tempFile := fs.NewFile(t, "config", fs.WithContent(content))
|
tempFile := fs.NewFile(t, "config", fs.WithContent(content))
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/docker/docker/daemon/config"
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
"github.com/docker/docker/pkg/homedir"
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
@ -72,6 +74,7 @@ type daemonOptions struct {
|
||||||
Debug bool
|
Debug bool
|
||||||
Hosts []string
|
Hosts []string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
|
LogFormat string
|
||||||
TLS bool
|
TLS bool
|
||||||
TLSVerify bool
|
TLSVerify bool
|
||||||
TLSOptions *tlsconfig.Options
|
TLSOptions *tlsconfig.Options
|
||||||
|
@ -104,6 +107,8 @@ func (o *daemonOptions) installFlags(flags *pflag.FlagSet) {
|
||||||
flags.BoolVarP(&o.Debug, "debug", "D", false, "Enable debug mode")
|
flags.BoolVarP(&o.Debug, "debug", "D", false, "Enable debug mode")
|
||||||
flags.BoolVar(&o.Validate, "validate", false, "Validate daemon configuration and exit")
|
flags.BoolVar(&o.Validate, "validate", false, "Validate daemon configuration and exit")
|
||||||
flags.StringVarP(&o.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`)
|
flags.StringVarP(&o.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`)
|
||||||
|
flags.StringVar(&o.LogFormat, "log-format", log.TextFormat,
|
||||||
|
fmt.Sprintf(`Set the logging format ("%s"|"%s")`, log.TextFormat, log.JSONFormat))
|
||||||
flags.BoolVar(&o.TLS, FlagTLS, DefaultTLSValue, "Use TLS; implied by --tlsverify")
|
flags.BoolVar(&o.TLS, FlagTLS, DefaultTLSValue, "Use TLS; implied by --tlsverify")
|
||||||
flags.BoolVar(&o.TLSVerify, FlagTLSVerify, dockerTLSVerify || DefaultTLSValue, "Use TLS and verify the remote")
|
flags.BoolVar(&o.TLSVerify, FlagTLSVerify, dockerTLSVerify || DefaultTLSValue, "Use TLS and verify the remote")
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@ type CommonConfig struct {
|
||||||
Debug bool `json:"debug,omitempty"`
|
Debug bool `json:"debug,omitempty"`
|
||||||
Hosts []string `json:"hosts,omitempty"`
|
Hosts []string `json:"hosts,omitempty"`
|
||||||
LogLevel string `json:"log-level,omitempty"`
|
LogLevel string `json:"log-level,omitempty"`
|
||||||
|
LogFormat string `json:"log-format,omitempty"`
|
||||||
TLS *bool `json:"tls,omitempty"`
|
TLS *bool `json:"tls,omitempty"`
|
||||||
TLSVerify *bool `json:"tlsverify,omitempty"`
|
TLSVerify *bool `json:"tlsverify,omitempty"`
|
||||||
|
|
||||||
|
@ -594,6 +595,16 @@ func Validate(config *Config) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate log-format
|
||||||
|
if logFormat := config.LogFormat; logFormat != "" {
|
||||||
|
switch logFormat {
|
||||||
|
case log.TextFormat, log.JSONFormat:
|
||||||
|
// These are valid
|
||||||
|
default:
|
||||||
|
return errors.Errorf("invalid log format: %s", logFormat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validate DNS
|
// validate DNS
|
||||||
for _, dns := range config.DNS {
|
for _, dns := range config.DNS {
|
||||||
if _, err := opts.ValidateIPAddress(dns); err != nil {
|
if _, err := opts.ValidateIPAddress(dns); err != nil {
|
||||||
|
|
|
@ -13,6 +13,15 @@ func WithLogLevel(lvl string) DaemonOpt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithLogFormat defines the containerd log format.
|
||||||
|
// This only makes sense if WithStartDaemon() was set to true.
|
||||||
|
func WithLogFormat(format string) DaemonOpt {
|
||||||
|
return func(r *remote) error {
|
||||||
|
r.Debug.Format = format
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithCRIDisabled disables the CRI plugin.
|
// WithCRIDisabled disables the CRI plugin.
|
||||||
func WithCRIDisabled() DaemonOpt {
|
func WithCRIDisabled() DaemonOpt {
|
||||||
return func(r *remote) error {
|
return func(r *remote) error {
|
||||||
|
|
Loading…
Reference in a new issue