mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
commit
1ff5a91007
23 changed files with 615 additions and 50 deletions
|
@ -1961,6 +1961,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if env.GetSubEnv("HostConfig").GetSubEnv("LogConfig").Get("Type") != "json-file" {
|
||||||
|
return fmt.Errorf("\"logs\" command is supported only for \"json-file\" logging driver")
|
||||||
|
}
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("stdout", "1")
|
v.Set("stdout", "1")
|
||||||
v.Set("stderr", "1")
|
v.Set("stderr", "1")
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/pkg/ulimit"
|
"github.com/docker/docker/pkg/ulimit"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -47,6 +48,7 @@ type Config struct {
|
||||||
TrustKeyPath string
|
TrustKeyPath string
|
||||||
Labels []string
|
Labels []string
|
||||||
Ulimits map[string]*ulimit.Ulimit
|
Ulimits map[string]*ulimit.Ulimit
|
||||||
|
LogConfig runconfig.LogConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstallFlags adds command-line options to the top-level flag parser for
|
// InstallFlags adds command-line options to the top-level flag parser for
|
||||||
|
@ -81,6 +83,7 @@ func (config *Config) InstallFlags() {
|
||||||
opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon")
|
opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon")
|
||||||
config.Ulimits = make(map[string]*ulimit.Ulimit)
|
config.Ulimits = make(map[string]*ulimit.Ulimit)
|
||||||
opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers")
|
opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers")
|
||||||
|
flag.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", "Containers logging driver(json-file/none)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultNetworkMtu() int {
|
func getDefaultNetworkMtu() int {
|
||||||
|
|
|
@ -21,6 +21,8 @@ import (
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
"github.com/docker/docker/daemon/logger"
|
||||||
|
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||||
"github.com/docker/docker/engine"
|
"github.com/docker/docker/engine"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/links"
|
"github.com/docker/docker/links"
|
||||||
|
@ -98,9 +100,11 @@ type Container struct {
|
||||||
VolumesRW map[string]bool
|
VolumesRW map[string]bool
|
||||||
hostConfig *runconfig.HostConfig
|
hostConfig *runconfig.HostConfig
|
||||||
|
|
||||||
activeLinks map[string]*links.Link
|
activeLinks map[string]*links.Link
|
||||||
monitor *containerMonitor
|
monitor *containerMonitor
|
||||||
execCommands *execStore
|
execCommands *execStore
|
||||||
|
// logDriver for closing
|
||||||
|
logDriver logger.Logger
|
||||||
AppliedVolumesFrom map[string]struct{}
|
AppliedVolumesFrom map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1355,21 +1359,36 @@ func (container *Container) setupWorkingDirectory() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) startLoggingToDisk() error {
|
func (container *Container) startLogging() error {
|
||||||
// Setup logging of stdout and stderr to disk
|
cfg := container.hostConfig.LogConfig
|
||||||
logPath, err := container.logPath("json")
|
if cfg.Type == "" {
|
||||||
if err != nil {
|
cfg = container.daemon.defaultLogConfig
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
container.LogPath = logPath
|
var l logger.Logger
|
||||||
|
switch cfg.Type {
|
||||||
|
case "json-file":
|
||||||
|
pth, err := container.logPath("json")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := container.daemon.LogToDisk(container.stdout, container.LogPath, "stdout"); err != nil {
|
dl, err := jsonfilelog.New(pth)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l = dl
|
||||||
|
case "none":
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unknown logging driver: %s", cfg.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := container.daemon.LogToDisk(container.stderr, container.LogPath, "stderr"); err != nil {
|
if copier, err := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l); err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else {
|
||||||
|
copier.Run()
|
||||||
}
|
}
|
||||||
|
container.logDriver = l
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1470,3 +1489,12 @@ func (container *Container) getNetworkedContainer() (*Container, error) {
|
||||||
func (container *Container) Stats() (*execdriver.ResourceStats, error) {
|
func (container *Container) Stats() (*execdriver.ResourceStats, error) {
|
||||||
return container.daemon.Stats(container)
|
return container.daemon.Stats(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) LogDriverType() string {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
if c.hostConfig.LogConfig.Type == "" {
|
||||||
|
return c.daemon.defaultLogConfig.Type
|
||||||
|
}
|
||||||
|
return c.hostConfig.LogConfig.Type
|
||||||
|
}
|
||||||
|
|
|
@ -89,23 +89,24 @@ func (c *contStore) List() []*Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Daemon struct {
|
type Daemon struct {
|
||||||
ID string
|
ID string
|
||||||
repository string
|
repository string
|
||||||
sysInitPath string
|
sysInitPath string
|
||||||
containers *contStore
|
containers *contStore
|
||||||
execCommands *execStore
|
execCommands *execStore
|
||||||
graph *graph.Graph
|
graph *graph.Graph
|
||||||
repositories *graph.TagStore
|
repositories *graph.TagStore
|
||||||
idIndex *truncindex.TruncIndex
|
idIndex *truncindex.TruncIndex
|
||||||
sysInfo *sysinfo.SysInfo
|
sysInfo *sysinfo.SysInfo
|
||||||
volumes *volumes.Repository
|
volumes *volumes.Repository
|
||||||
eng *engine.Engine
|
eng *engine.Engine
|
||||||
config *Config
|
config *Config
|
||||||
containerGraph *graphdb.Database
|
containerGraph *graphdb.Database
|
||||||
driver graphdriver.Driver
|
driver graphdriver.Driver
|
||||||
execDriver execdriver.Driver
|
execDriver execdriver.Driver
|
||||||
trustStore *trust.TrustStore
|
trustStore *trust.TrustStore
|
||||||
statsCollector *statsCollector
|
statsCollector *statsCollector
|
||||||
|
defaultLogConfig runconfig.LogConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install installs daemon capabilities to eng.
|
// Install installs daemon capabilities to eng.
|
||||||
|
@ -991,23 +992,24 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
daemon := &Daemon{
|
daemon := &Daemon{
|
||||||
ID: trustKey.PublicKey().KeyID(),
|
ID: trustKey.PublicKey().KeyID(),
|
||||||
repository: daemonRepo,
|
repository: daemonRepo,
|
||||||
containers: &contStore{s: make(map[string]*Container)},
|
containers: &contStore{s: make(map[string]*Container)},
|
||||||
execCommands: newExecStore(),
|
execCommands: newExecStore(),
|
||||||
graph: g,
|
graph: g,
|
||||||
repositories: repositories,
|
repositories: repositories,
|
||||||
idIndex: truncindex.NewTruncIndex([]string{}),
|
idIndex: truncindex.NewTruncIndex([]string{}),
|
||||||
sysInfo: sysInfo,
|
sysInfo: sysInfo,
|
||||||
volumes: volumes,
|
volumes: volumes,
|
||||||
config: config,
|
config: config,
|
||||||
containerGraph: graph,
|
containerGraph: graph,
|
||||||
driver: driver,
|
driver: driver,
|
||||||
sysInitPath: sysInitPath,
|
sysInitPath: sysInitPath,
|
||||||
execDriver: ed,
|
execDriver: ed,
|
||||||
eng: eng,
|
eng: eng,
|
||||||
trustStore: t,
|
trustStore: t,
|
||||||
statsCollector: newStatsCollector(1 * time.Second),
|
statsCollector: newStatsCollector(1 * time.Second),
|
||||||
|
defaultLogConfig: config.LogConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup shutdown handlers
|
// Setup shutdown handlers
|
||||||
|
|
|
@ -62,6 +62,14 @@ func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status {
|
||||||
container.hostConfig.Links = append(container.hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
|
container.hostConfig.Links = append(container.hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// we need this trick to preserve empty log driver, so
|
||||||
|
// container will use daemon defaults even if daemon change them
|
||||||
|
if container.hostConfig.LogConfig.Type == "" {
|
||||||
|
container.hostConfig.LogConfig = daemon.defaultLogConfig
|
||||||
|
defer func() {
|
||||||
|
container.hostConfig.LogConfig = runconfig.LogConfig{}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
out.SetJson("HostConfig", container.hostConfig)
|
out.SetJson("HostConfig", container.hostConfig)
|
||||||
|
|
||||||
|
|
48
daemon/logger/copier.go
Normal file
48
daemon/logger/copier.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copier can copy logs from specified sources to Logger and attach
|
||||||
|
// ContainerID and Timestamp.
|
||||||
|
// Writes are concurrent, so you need implement some sync in your logger
|
||||||
|
type Copier struct {
|
||||||
|
// cid is container id for which we copying logs
|
||||||
|
cid string
|
||||||
|
// srcs is map of name -> reader pairs, for example "stdout", "stderr"
|
||||||
|
srcs map[string]io.Reader
|
||||||
|
dst Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCopier creates new Copier
|
||||||
|
func NewCopier(cid string, srcs map[string]io.Reader, dst Logger) (*Copier, error) {
|
||||||
|
return &Copier{
|
||||||
|
cid: cid,
|
||||||
|
srcs: srcs,
|
||||||
|
dst: dst,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts logs copying
|
||||||
|
func (c *Copier) Run() {
|
||||||
|
for src, w := range c.srcs {
|
||||||
|
go c.copySrc(src, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Copier) copySrc(name string, src io.Reader) {
|
||||||
|
scanner := bufio.NewScanner(src)
|
||||||
|
for scanner.Scan() {
|
||||||
|
if err := c.dst.Log(&Message{ContainerID: c.cid, Line: scanner.Bytes(), Source: name, Timestamp: time.Now().UTC()}); err != nil {
|
||||||
|
logrus.Errorf("Failed to log msg %q for logger %s: %s", scanner.Bytes(), c.dst.Name(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logrus.Errorf("Error scanning log stream: %s", err)
|
||||||
|
}
|
||||||
|
}
|
100
daemon/logger/copier_test.go
Normal file
100
daemon/logger/copier_test.go
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestLoggerJSON struct {
|
||||||
|
*json.Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestLoggerJSON) Log(m *Message) error {
|
||||||
|
return l.Encode(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestLoggerJSON) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestLoggerJSON) Name() string {
|
||||||
|
return "json"
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestLoggerText struct {
|
||||||
|
*bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestLoggerText) Log(m *Message) error {
|
||||||
|
_, err := l.WriteString(m.ContainerID + " " + m.Source + " " + string(m.Line) + "\n")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestLoggerText) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestLoggerText) Name() string {
|
||||||
|
return "text"
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopier(t *testing.T) {
|
||||||
|
stdoutLine := "Line that thinks that it is log line from docker stdout"
|
||||||
|
stderrLine := "Line that thinks that it is log line from docker stderr"
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
for i := 0; i < 30; i++ {
|
||||||
|
if _, err := stdout.WriteString(stdoutLine + "\n"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := stderr.WriteString(stderrLine + "\n"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonBuf bytes.Buffer
|
||||||
|
|
||||||
|
jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}
|
||||||
|
|
||||||
|
cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657"
|
||||||
|
c, err := NewCopier(cid,
|
||||||
|
map[string]io.Reader{
|
||||||
|
"stdout": &stdout,
|
||||||
|
"stderr": &stderr,
|
||||||
|
},
|
||||||
|
jsonLog)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c.Run()
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
dec := json.NewDecoder(&jsonBuf)
|
||||||
|
for {
|
||||||
|
var msg Message
|
||||||
|
if err := dec.Decode(&msg); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if msg.Source != "stdout" && msg.Source != "stderr" {
|
||||||
|
t.Fatalf("Wrong Source: %q, should be %q or %q", msg.Source, "stdout", "stderr")
|
||||||
|
}
|
||||||
|
if msg.ContainerID != cid {
|
||||||
|
t.Fatalf("Wrong ContainerID: %q, expected %q", msg.ContainerID, cid)
|
||||||
|
}
|
||||||
|
if msg.Source == "stdout" {
|
||||||
|
if string(msg.Line) != stdoutLine {
|
||||||
|
t.Fatalf("Wrong Line: %q, expected %q", msg.Line, stdoutLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msg.Source == "stderr" {
|
||||||
|
if string(msg.Line) != stderrLine {
|
||||||
|
t.Fatalf("Wrong Line: %q, expected %q", msg.Line, stderrLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
daemon/logger/jsonfilelog/jsonfilelog.go
Normal file
49
daemon/logger/jsonfilelog/jsonfilelog.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package jsonfilelog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/daemon/logger"
|
||||||
|
"github.com/docker/docker/pkg/jsonlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONFileLogger is Logger implementation for default docker logging:
|
||||||
|
// JSON objects to file
|
||||||
|
type JSONFileLogger struct {
|
||||||
|
buf *bytes.Buffer
|
||||||
|
f *os.File // store for closing
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates new JSONFileLogger which writes to filename
|
||||||
|
func New(filename string) (logger.Logger, error) {
|
||||||
|
log, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JSONFileLogger{
|
||||||
|
f: log,
|
||||||
|
buf: bytes.NewBuffer(nil),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log converts logger.Message to jsonlog.JSONLog and serializes it to file
|
||||||
|
func (l *JSONFileLogger) Log(msg *logger.Message) error {
|
||||||
|
err := (&jsonlog.JSONLog{Log: string(msg.Line) + "\n", Stream: msg.Source, Created: msg.Timestamp}).MarshalJSONBuf(l.buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l.buf.WriteByte('\n')
|
||||||
|
_, err = l.buf.WriteTo(l.f)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes underlying file
|
||||||
|
func (l *JSONFileLogger) Close() error {
|
||||||
|
return l.f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns name of this logger
|
||||||
|
func (l *JSONFileLogger) Name() string {
|
||||||
|
return "JSONFile"
|
||||||
|
}
|
78
daemon/logger/jsonfilelog/jsonfilelog_test.go
Normal file
78
daemon/logger/jsonfilelog/jsonfilelog_test.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package jsonfilelog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/daemon/logger"
|
||||||
|
"github.com/docker/docker/pkg/jsonlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJSONFileLogger(t *testing.T) {
|
||||||
|
tmp, err := ioutil.TempDir("", "docker-logger-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmp)
|
||||||
|
filename := filepath.Join(tmp, "container.log")
|
||||||
|
l, err := New(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657"
|
||||||
|
if err := l.Log(&logger.Message{ContainerID: cid, Line: []byte("line1"), Source: "src1"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := l.Log(&logger.Message{ContainerID: cid, Line: []byte("line2"), Source: "src2"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := l.Log(&logger.Message{ContainerID: cid, Line: []byte("line3"), Source: "src3"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
res, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expected := `{"log":"line1\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||||
|
{"log":"line2\n","stream":"src2","time":"0001-01-01T00:00:00Z"}
|
||||||
|
{"log":"line3\n","stream":"src3","time":"0001-01-01T00:00:00Z"}
|
||||||
|
`
|
||||||
|
|
||||||
|
if string(res) != expected {
|
||||||
|
t.Fatalf("Wrong log content: %q, expected %q", res, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkJSONFileLogger(b *testing.B) {
|
||||||
|
tmp, err := ioutil.TempDir("", "docker-logger-")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmp)
|
||||||
|
filename := filepath.Join(tmp, "container.log")
|
||||||
|
l, err := New(filename)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657"
|
||||||
|
testLine := "Line that thinks that it is log line from docker\n"
|
||||||
|
msg := &logger.Message{ContainerID: cid, Line: []byte(testLine), Source: "stderr", Timestamp: time.Now().UTC()}
|
||||||
|
jsonlog, err := (&jsonlog.JSONLog{Log: string(msg.Line) + "\n", Stream: msg.Source, Created: msg.Timestamp}).MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(len(jsonlog)+1) * 30)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := 0; j < 30; j++ {
|
||||||
|
if err := l.Log(msg); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
daemon/logger/logger.go
Normal file
18
daemon/logger/logger.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Message is datastructure that represents record from some container
|
||||||
|
type Message struct {
|
||||||
|
ContainerID string
|
||||||
|
Line []byte
|
||||||
|
Source string
|
||||||
|
Timestamp time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger is interface for docker logging drivers
|
||||||
|
type Logger interface {
|
||||||
|
Log(*Message) error
|
||||||
|
Name() string
|
||||||
|
Close() error
|
||||||
|
}
|
|
@ -44,6 +44,9 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
if container.LogDriverType() != "json-file" {
|
||||||
|
return job.Errorf("\"logs\" endpoint is supported only for \"json-file\" logging driver")
|
||||||
|
}
|
||||||
cLog, err := container.ReadLog("json")
|
cLog, err := container.ReadLog("json")
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(err) {
|
||||||
// Legacy logs
|
// Legacy logs
|
||||||
|
|
|
@ -123,7 +123,7 @@ func (m *containerMonitor) Start() error {
|
||||||
for {
|
for {
|
||||||
m.container.RestartCount++
|
m.container.RestartCount++
|
||||||
|
|
||||||
if err := m.container.startLoggingToDisk(); err != nil {
|
if err := m.container.startLogging(); err != nil {
|
||||||
m.resetContainer(false)
|
m.resetContainer(false)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -302,6 +302,11 @@ func (m *containerMonitor) resetContainer(lock bool) {
|
||||||
container.stdin, container.stdinPipe = io.Pipe()
|
container.stdin, container.stdinPipe = io.Pipe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if container.logDriver != nil {
|
||||||
|
container.logDriver.Close()
|
||||||
|
container.logDriver = nil
|
||||||
|
}
|
||||||
|
|
||||||
c := container.command.ProcessConfig.Cmd
|
c := container.command.ProcessConfig.Cmd
|
||||||
|
|
||||||
container.command.ProcessConfig.Cmd = exec.Cmd{
|
container.command.ProcessConfig.Cmd = exec.Cmd{
|
||||||
|
|
|
@ -28,6 +28,7 @@ docker-create - Create a new container
|
||||||
[**--label-file**[=*[]*]]
|
[**--label-file**[=*[]*]]
|
||||||
[**--link**[=*[]*]]
|
[**--link**[=*[]*]]
|
||||||
[**--lxc-conf**[=*[]*]]
|
[**--lxc-conf**[=*[]*]]
|
||||||
|
[**--log-driver**[=*[]*]]
|
||||||
[**-m**|**--memory**[=*MEMORY*]]
|
[**-m**|**--memory**[=*MEMORY*]]
|
||||||
[**--memory-swap**[=*MEMORY-SWAP*]]
|
[**--memory-swap**[=*MEMORY-SWAP*]]
|
||||||
[**--mac-address**[=*MAC-ADDRESS*]]
|
[**--mac-address**[=*MAC-ADDRESS*]]
|
||||||
|
@ -116,6 +117,10 @@ IMAGE [COMMAND] [ARG...]
|
||||||
**--lxc-conf**=[]
|
**--lxc-conf**=[]
|
||||||
(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
||||||
|
|
||||||
|
**--log-driver**="|*json-file*|*none*"
|
||||||
|
Logging driver for container. Default is defined by daemon `--log-driver` flag.
|
||||||
|
**Warning**: `docker logs` command works only for `json-file` logging driver.
|
||||||
|
|
||||||
**-m**, **--memory**=""
|
**-m**, **--memory**=""
|
||||||
Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@ The **docker logs --follow** command combines commands **docker logs** and
|
||||||
**docker attach**. It will first return all logs from the beginning and
|
**docker attach**. It will first return all logs from the beginning and
|
||||||
then continue streaming new output from the container’s stdout and stderr.
|
then continue streaming new output from the container’s stdout and stderr.
|
||||||
|
|
||||||
|
**Warning**: This command works only for **json-file** logging driver.
|
||||||
|
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
**--help**
|
**--help**
|
||||||
Print usage statement
|
Print usage statement
|
||||||
|
|
|
@ -29,6 +29,7 @@ docker-run - Run a command in a new container
|
||||||
[**--label-file**[=*[]*]]
|
[**--label-file**[=*[]*]]
|
||||||
[**--link**[=*[]*]]
|
[**--link**[=*[]*]]
|
||||||
[**--lxc-conf**[=*[]*]]
|
[**--lxc-conf**[=*[]*]]
|
||||||
|
[**--log-driver**[=*[]*]]
|
||||||
[**-m**|**--memory**[=*MEMORY*]]
|
[**-m**|**--memory**[=*MEMORY*]]
|
||||||
[**--memory-swap**[=*MEMORY-SWAP]]
|
[**--memory-swap**[=*MEMORY-SWAP]]
|
||||||
[**--mac-address**[=*MAC-ADDRESS*]]
|
[**--mac-address**[=*MAC-ADDRESS*]]
|
||||||
|
@ -217,6 +218,10 @@ which interface and port to use.
|
||||||
**--lxc-conf**=[]
|
**--lxc-conf**=[]
|
||||||
(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
||||||
|
|
||||||
|
**--log-driver**="|*json-file*|*none*"
|
||||||
|
Logging driver for container. Default is defined by daemon `--log-driver` flag.
|
||||||
|
**Warning**: `docker logs` command works only for `json-file` logging driver.
|
||||||
|
|
||||||
**-m**, **--memory**=""
|
**-m**, **--memory**=""
|
||||||
Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,10 @@ unix://[/path/to/socket] to use.
|
||||||
**--label**="[]"
|
**--label**="[]"
|
||||||
Set key=value labels to the daemon (displayed in `docker info`)
|
Set key=value labels to the daemon (displayed in `docker info`)
|
||||||
|
|
||||||
|
**--log-driver**="*json-file*|*none*"
|
||||||
|
Container's logging driver. Default is `default`.
|
||||||
|
**Warning**: `docker logs` command works only for `json-file` logging driver.
|
||||||
|
|
||||||
**--mtu**=VALUE
|
**--mtu**=VALUE
|
||||||
Set the containers network mtu. Default is `0`.
|
Set the containers network mtu. Default is `0`.
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,8 @@ Create a container
|
||||||
"RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
|
"RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
|
||||||
"NetworkMode": "bridge",
|
"NetworkMode": "bridge",
|
||||||
"Devices": [],
|
"Devices": [],
|
||||||
"Ulimits": [{}]
|
"Ulimits": [{}],
|
||||||
|
"LogConfig": { "Type": "json-file", Config: {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,6 +256,10 @@ Json Parameters:
|
||||||
- **Ulimits** - A list of ulimits to be set in the container, specified as
|
- **Ulimits** - A list of ulimits to be set in the container, specified as
|
||||||
`{ "Name": <name>, "Soft": <soft limit>, "Hard": <hard limit> }`, for example:
|
`{ "Name": <name>, "Soft": <soft limit>, "Hard": <hard limit> }`, for example:
|
||||||
`Ulimits: { "Name": "nofile", "Soft": 1024, "Hard", 2048 }}`
|
`Ulimits: { "Name": "nofile", "Soft": 1024, "Hard", 2048 }}`
|
||||||
|
- **LogConfig** - Logging configuration to container, format
|
||||||
|
`{ "Type": "<driver_name>", "Config": {"key1": "val1"}}
|
||||||
|
Available types: `json-file`, `none`.
|
||||||
|
`json-file` logging driver.
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
@ -352,6 +357,7 @@ Return low-level information on the container `id`
|
||||||
"MaximumRetryCount": 2,
|
"MaximumRetryCount": 2,
|
||||||
"Name": "on-failure"
|
"Name": "on-failure"
|
||||||
},
|
},
|
||||||
|
"LogConfig": { "Type": "json-file", Config: {} },
|
||||||
"SecurityOpt": null,
|
"SecurityOpt": null,
|
||||||
"VolumesFrom": null,
|
"VolumesFrom": null,
|
||||||
"Ulimits": [{}]
|
"Ulimits": [{}]
|
||||||
|
@ -448,6 +454,9 @@ Status Codes:
|
||||||
|
|
||||||
Get stdout and stderr logs from the container ``id``
|
Get stdout and stderr logs from the container ``id``
|
||||||
|
|
||||||
|
> **Note**:
|
||||||
|
> This endpoint works only for containers with `json-file` logging driver.
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1&tail=10 HTTP/1.1
|
GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1&tail=10 HTTP/1.1
|
||||||
|
|
|
@ -97,6 +97,7 @@ expect an integer, and they can only be specified once.
|
||||||
--ipv6=false Enable IPv6 networking
|
--ipv6=false Enable IPv6 networking
|
||||||
-l, --log-level="info" Set the logging level
|
-l, --log-level="info" Set the logging level
|
||||||
--label=[] Set key=value labels to the daemon
|
--label=[] Set key=value labels to the daemon
|
||||||
|
--log-driver="json-file" Container's logging driver (json-file/none)
|
||||||
--mtu=0 Set the containers network MTU
|
--mtu=0 Set the containers network MTU
|
||||||
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file
|
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file
|
||||||
--registry-mirror=[] Preferred Docker registry mirror
|
--registry-mirror=[] Preferred Docker registry mirror
|
||||||
|
@ -817,6 +818,7 @@ Creates a new container.
|
||||||
-l, --label=[] Set metadata on the container (e.g., --label=com.example.key=value)
|
-l, --label=[] Set metadata on the container (e.g., --label=com.example.key=value)
|
||||||
--label-file=[] Read in a line delimited file of labels
|
--label-file=[] Read in a line delimited file of labels
|
||||||
--link=[] Add link to another container
|
--link=[] Add link to another container
|
||||||
|
--log-driver="" Logging driver for container
|
||||||
--lxc-conf=[] Add custom lxc options
|
--lxc-conf=[] Add custom lxc options
|
||||||
-m, --memory="" Memory limit
|
-m, --memory="" Memory limit
|
||||||
--mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33)
|
--mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33)
|
||||||
|
@ -1447,6 +1449,9 @@ For example:
|
||||||
-t, --timestamps=false Show timestamps
|
-t, --timestamps=false Show timestamps
|
||||||
--tail="all" Number of lines to show from the end of the logs
|
--tail="all" Number of lines to show from the end of the logs
|
||||||
|
|
||||||
|
NOTE: this command is available only for containers with `json-file` logging
|
||||||
|
driver.
|
||||||
|
|
||||||
The `docker logs` command batch-retrieves logs present at the time of execution.
|
The `docker logs` command batch-retrieves logs present at the time of execution.
|
||||||
|
|
||||||
The `docker logs --follow` command will continue streaming the new output from
|
The `docker logs --follow` command will continue streaming the new output from
|
||||||
|
@ -1722,6 +1727,7 @@ To remove an image using its digest:
|
||||||
-i, --interactive=false Keep STDIN open even if not attached
|
-i, --interactive=false Keep STDIN open even if not attached
|
||||||
--ipc="" IPC namespace to use
|
--ipc="" IPC namespace to use
|
||||||
--link=[] Add link to another container
|
--link=[] Add link to another container
|
||||||
|
--log-driver="" Logging driver for container
|
||||||
--lxc-conf=[] Add custom lxc options
|
--lxc-conf=[] Add custom lxc options
|
||||||
-m, --memory="" Memory limit
|
-m, --memory="" Memory limit
|
||||||
-l, --label=[] Set metadata on the container (e.g., --label=com.example.key=value)
|
-l, --label=[] Set metadata on the container (e.g., --label=com.example.key=value)
|
||||||
|
|
|
@ -642,6 +642,20 @@ familiar with using LXC directly.
|
||||||
> you can use `--lxc-conf` to set a container's IP address, but this will not be
|
> you can use `--lxc-conf` to set a container's IP address, but this will not be
|
||||||
> reflected in the `/etc/hosts` file.
|
> reflected in the `/etc/hosts` file.
|
||||||
|
|
||||||
|
## Logging drivers (--log-driver)
|
||||||
|
|
||||||
|
You can specify a different logging driver for the container than for the daemon.
|
||||||
|
|
||||||
|
### Logging driver: none
|
||||||
|
|
||||||
|
Disables any logging for the container. `docker logs` won't be available with
|
||||||
|
this driver.
|
||||||
|
|
||||||
|
### Log driver: json-file
|
||||||
|
|
||||||
|
Default logging driver for Docker. Writes JSON messages to file. `docker logs`
|
||||||
|
command is available only for this logging driver
|
||||||
|
|
||||||
## Overriding Dockerfile image defaults
|
## Overriding Dockerfile image defaults
|
||||||
|
|
||||||
When a developer builds an image from a [*Dockerfile*](/reference/builder)
|
When a developer builds an image from a [*Dockerfile*](/reference/builder)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
)
|
)
|
||||||
|
@ -560,3 +561,168 @@ func TestDaemonRestartRenameContainer(t *testing.T) {
|
||||||
|
|
||||||
logDone("daemon - rename persists through daemon restart")
|
logDone("daemon - rename persists through daemon restart")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDaemonLoggingDriverDefault(t *testing.T) {
|
||||||
|
d := NewDaemon(t)
|
||||||
|
|
||||||
|
if err := d.StartWithBusybox(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer d.Stop()
|
||||||
|
|
||||||
|
out, err := d.Cmd("run", "-d", "busybox", "echo", "testline")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
id := strings.TrimSpace(out)
|
||||||
|
|
||||||
|
if out, err := d.Cmd("wait", id); err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log")
|
||||||
|
|
||||||
|
if _, err := os.Stat(logPath); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f, err := os.Open(logPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var res struct {
|
||||||
|
Log string `json:log`
|
||||||
|
Stream string `json:stream`
|
||||||
|
Time time.Time `json:time`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(f).Decode(&res); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if res.Log != "testline\n" {
|
||||||
|
t.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n")
|
||||||
|
}
|
||||||
|
if res.Stream != "stdout" {
|
||||||
|
t.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout")
|
||||||
|
}
|
||||||
|
if !time.Now().After(res.Time) {
|
||||||
|
t.Fatalf("Log time %v in future", res.Time)
|
||||||
|
}
|
||||||
|
logDone("daemon - default 'json-file' logging driver")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaemonLoggingDriverDefaultOverride(t *testing.T) {
|
||||||
|
d := NewDaemon(t)
|
||||||
|
|
||||||
|
if err := d.StartWithBusybox(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer d.Stop()
|
||||||
|
|
||||||
|
out, err := d.Cmd("run", "-d", "--log-driver=none", "busybox", "echo", "testline")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
id := strings.TrimSpace(out)
|
||||||
|
|
||||||
|
if out, err := d.Cmd("wait", id); err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log")
|
||||||
|
|
||||||
|
if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) {
|
||||||
|
t.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err)
|
||||||
|
}
|
||||||
|
logDone("daemon - default logging driver override in run")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaemonLoggingDriverNone(t *testing.T) {
|
||||||
|
d := NewDaemon(t)
|
||||||
|
|
||||||
|
if err := d.StartWithBusybox("--log-driver=none"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer d.Stop()
|
||||||
|
|
||||||
|
out, err := d.Cmd("run", "-d", "busybox", "echo", "testline")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
id := strings.TrimSpace(out)
|
||||||
|
if out, err := d.Cmd("wait", id); err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log")
|
||||||
|
|
||||||
|
if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) {
|
||||||
|
t.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err)
|
||||||
|
}
|
||||||
|
logDone("daemon - 'none' logging driver")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaemonLoggingDriverNoneOverride(t *testing.T) {
|
||||||
|
d := NewDaemon(t)
|
||||||
|
|
||||||
|
if err := d.StartWithBusybox("--log-driver=none"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer d.Stop()
|
||||||
|
|
||||||
|
out, err := d.Cmd("run", "-d", "--log-driver=json-file", "busybox", "echo", "testline")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
id := strings.TrimSpace(out)
|
||||||
|
|
||||||
|
if out, err := d.Cmd("wait", id); err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log")
|
||||||
|
|
||||||
|
if _, err := os.Stat(logPath); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f, err := os.Open(logPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var res struct {
|
||||||
|
Log string `json:log`
|
||||||
|
Stream string `json:stream`
|
||||||
|
Time time.Time `json:time`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(f).Decode(&res); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if res.Log != "testline\n" {
|
||||||
|
t.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n")
|
||||||
|
}
|
||||||
|
if res.Stream != "stdout" {
|
||||||
|
t.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout")
|
||||||
|
}
|
||||||
|
if !time.Now().After(res.Time) {
|
||||||
|
t.Fatalf("Log time %v in future", res.Time)
|
||||||
|
}
|
||||||
|
logDone("daemon - 'none' logging driver override in run")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaemonLoggingDriverNoneLogsError(t *testing.T) {
|
||||||
|
d := NewDaemon(t)
|
||||||
|
|
||||||
|
if err := d.StartWithBusybox("--log-driver=none"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer d.Stop()
|
||||||
|
|
||||||
|
out, err := d.Cmd("run", "-d", "busybox", "echo", "testline")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(out, err)
|
||||||
|
}
|
||||||
|
id := strings.TrimSpace(out)
|
||||||
|
out, err = d.Cmd("logs", id)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Logs should fail with \"none\" driver")
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, `\"logs\" command is supported only for \"json-file\" logging driver`) {
|
||||||
|
t.Fatalf("There should be error about non-json-file driver, got %s", out)
|
||||||
|
}
|
||||||
|
logDone("daemon - logs not available for non-json-file drivers")
|
||||||
|
}
|
||||||
|
|
|
@ -191,6 +191,7 @@ func newTestEngine(t Fataler, autorestart bool, root string) *engine.Engine {
|
||||||
// otherwise NewDaemon will fail because of conflicting settings.
|
// otherwise NewDaemon will fail because of conflicting settings.
|
||||||
InterContainerCommunication: true,
|
InterContainerCommunication: true,
|
||||||
TrustKeyPath: filepath.Join(root, "key.json"),
|
TrustKeyPath: filepath.Join(root, "key.json"),
|
||||||
|
LogConfig: runconfig.LogConfig{Type: "json-file"},
|
||||||
}
|
}
|
||||||
d, err := daemon.NewDaemon(cfg, eng)
|
d, err := daemon.NewDaemon(cfg, eng)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -99,6 +99,11 @@ type RestartPolicy struct {
|
||||||
MaximumRetryCount int
|
MaximumRetryCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogConfig struct {
|
||||||
|
Type string
|
||||||
|
Config map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
type HostConfig struct {
|
type HostConfig struct {
|
||||||
Binds []string
|
Binds []string
|
||||||
ContainerIDFile string
|
ContainerIDFile string
|
||||||
|
@ -125,6 +130,7 @@ type HostConfig struct {
|
||||||
SecurityOpt []string
|
SecurityOpt []string
|
||||||
ReadonlyRootfs bool
|
ReadonlyRootfs bool
|
||||||
Ulimits []*ulimit.Ulimit
|
Ulimits []*ulimit.Ulimit
|
||||||
|
LogConfig LogConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used by the create command when you want to set both the
|
// This is used by the create command when you want to set both the
|
||||||
|
@ -189,9 +195,8 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
|
||||||
job.GetenvJson("PortBindings", &hostConfig.PortBindings)
|
job.GetenvJson("PortBindings", &hostConfig.PortBindings)
|
||||||
job.GetenvJson("Devices", &hostConfig.Devices)
|
job.GetenvJson("Devices", &hostConfig.Devices)
|
||||||
job.GetenvJson("RestartPolicy", &hostConfig.RestartPolicy)
|
job.GetenvJson("RestartPolicy", &hostConfig.RestartPolicy)
|
||||||
|
|
||||||
job.GetenvJson("Ulimits", &hostConfig.Ulimits)
|
job.GetenvJson("Ulimits", &hostConfig.Ulimits)
|
||||||
|
job.GetenvJson("LogConfig", &hostConfig.LogConfig)
|
||||||
hostConfig.SecurityOpt = job.GetenvList("SecurityOpt")
|
hostConfig.SecurityOpt = job.GetenvList("SecurityOpt")
|
||||||
if Binds := job.GetenvList("Binds"); Binds != nil {
|
if Binds := job.GetenvList("Binds"); Binds != nil {
|
||||||
hostConfig.Binds = Binds
|
hostConfig.Binds = Binds
|
||||||
|
|
|
@ -70,6 +70,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||||
flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
||||||
flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
|
flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
|
||||||
flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only")
|
flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only")
|
||||||
|
flLoggingDriver = cmd.String([]string{"-log-driver"}, "", "Logging driver for container")
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
||||||
|
@ -330,6 +331,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||||
SecurityOpt: flSecurityOpt.GetAll(),
|
SecurityOpt: flSecurityOpt.GetAll(),
|
||||||
ReadonlyRootfs: *flReadonlyRootfs,
|
ReadonlyRootfs: *flReadonlyRootfs,
|
||||||
Ulimits: flUlimits.GetList(),
|
Ulimits: flUlimits.GetList(),
|
||||||
|
LogConfig: LogConfig{Type: *flLoggingDriver},
|
||||||
}
|
}
|
||||||
|
|
||||||
// When allocating stdin in attached mode, close stdin at client disconnect
|
// When allocating stdin in attached mode, close stdin at client disconnect
|
||||||
|
|
Loading…
Add table
Reference in a new issue