- noplog driver pkg for '--log-driver=none' (null object pattern)
- centralized factory for log drivers (instead of case/switch)
- logging drivers registers themselves to factory upon import
(easy plug/unplug of drivers in daemon/logdrivers.go)
- daemon now doesn't start with an invalid log driver
- Name() method of loggers is actually now their cli names (made it useful)
- generalized Read() logic, made it unsupported except json-file (preserves
existing behavior)
Spotted some duplication code around processing of legacy json-file
format, didn't touch that and refactored in both places.
Signed-off-by: Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
... | ... |
@@ -37,8 +37,8 @@ func (cli *DockerCli) CmdLogs(args ...string) error { |
37 | 37 |
return err |
38 | 38 |
} |
39 | 39 |
|
40 |
- if c.HostConfig.LogConfig.Type != "json-file" { |
|
41 |
- return fmt.Errorf("\"logs\" command is supported only for \"json-file\" logging driver") |
|
40 |
+ if logType := c.HostConfig.LogConfig.Type; logType != "json-file" { |
|
41 |
+ return fmt.Errorf("\"logs\" command is supported only for \"json-file\" logging driver (got: %s)", logType) |
|
42 | 42 |
} |
43 | 43 |
|
44 | 44 |
v := url.Values{} |
... | ... |
@@ -22,9 +22,7 @@ import ( |
22 | 22 |
"github.com/Sirupsen/logrus" |
23 | 23 |
"github.com/docker/docker/daemon/execdriver" |
24 | 24 |
"github.com/docker/docker/daemon/logger" |
25 |
- "github.com/docker/docker/daemon/logger/journald" |
|
26 | 25 |
"github.com/docker/docker/daemon/logger/jsonfilelog" |
27 |
- "github.com/docker/docker/daemon/logger/syslog" |
|
28 | 26 |
"github.com/docker/docker/daemon/network" |
29 | 27 |
"github.com/docker/docker/daemon/networkdriver/bridge" |
30 | 28 |
"github.com/docker/docker/image" |
... | ... |
@@ -947,18 +945,6 @@ func (container *Container) Unmount() error { |
947 | 947 |
return container.daemon.Unmount(container) |
948 | 948 |
} |
949 | 949 |
|
950 |
-func (container *Container) logPath(name string) (string, error) { |
|
951 |
- return container.GetRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name)) |
|
952 |
-} |
|
953 |
- |
|
954 |
-func (container *Container) ReadLog(name string) (io.Reader, error) { |
|
955 |
- pth, err := container.logPath(name) |
|
956 |
- if err != nil { |
|
957 |
- return nil, err |
|
958 |
- } |
|
959 |
- return os.Open(pth) |
|
960 |
-} |
|
961 |
- |
|
962 | 950 |
func (container *Container) hostConfigPath() (string, error) { |
963 | 951 |
return container.GetRootResourcePath("hostconfig.json") |
964 | 952 |
} |
... | ... |
@@ -1445,41 +1431,45 @@ func (container *Container) setupWorkingDirectory() error { |
1445 | 1445 |
return nil |
1446 | 1446 |
} |
1447 | 1447 |
|
1448 |
-func (container *Container) startLogging() error { |
|
1448 |
+func (container *Container) getLogConfig() runconfig.LogConfig { |
|
1449 | 1449 |
cfg := container.hostConfig.LogConfig |
1450 |
- if cfg.Type == "" { |
|
1451 |
- cfg = container.daemon.defaultLogConfig |
|
1450 |
+ if cfg.Type != "" { // container has log driver configured |
|
1451 |
+ return cfg |
|
1452 | 1452 |
} |
1453 |
- var l logger.Logger |
|
1454 |
- switch cfg.Type { |
|
1455 |
- case "json-file": |
|
1456 |
- pth, err := container.logPath("json") |
|
1457 |
- if err != nil { |
|
1458 |
- return err |
|
1459 |
- } |
|
1460 |
- container.LogPath = pth |
|
1453 |
+ // Use daemon's default log config for containers |
|
1454 |
+ return container.daemon.defaultLogConfig |
|
1455 |
+} |
|
1461 | 1456 |
|
1462 |
- dl, err := jsonfilelog.New(pth) |
|
1463 |
- if err != nil { |
|
1464 |
- return err |
|
1465 |
- } |
|
1466 |
- l = dl |
|
1467 |
- case "syslog": |
|
1468 |
- dl, err := syslog.New(container.ID[:12]) |
|
1469 |
- if err != nil { |
|
1470 |
- return err |
|
1471 |
- } |
|
1472 |
- l = dl |
|
1473 |
- case "journald": |
|
1474 |
- dl, err := journald.New(container.ID, container.Name) |
|
1457 |
+func (container *Container) getLogger() (logger.Logger, error) { |
|
1458 |
+ cfg := container.getLogConfig() |
|
1459 |
+ c, err := logger.GetLogDriver(cfg.Type) |
|
1460 |
+ if err != nil { |
|
1461 |
+ return nil, fmt.Errorf("Failed to get logging factory: %v", err) |
|
1462 |
+ } |
|
1463 |
+ ctx := logger.Context{ |
|
1464 |
+ ContainerID: container.ID, |
|
1465 |
+ ContainerName: container.Name, |
|
1466 |
+ } |
|
1467 |
+ |
|
1468 |
+ // Set logging file for "json-logger" |
|
1469 |
+ if cfg.Type == jsonfilelog.Name { |
|
1470 |
+ ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID)) |
|
1475 | 1471 |
if err != nil { |
1476 |
- return err |
|
1472 |
+ return nil, err |
|
1477 | 1473 |
} |
1478 |
- l = dl |
|
1479 |
- case "none": |
|
1480 |
- return nil |
|
1481 |
- default: |
|
1482 |
- return fmt.Errorf("Unknown logging driver: %s", cfg.Type) |
|
1474 |
+ } |
|
1475 |
+ return c(ctx) |
|
1476 |
+} |
|
1477 |
+ |
|
1478 |
+func (container *Container) startLogging() error { |
|
1479 |
+ cfg := container.getLogConfig() |
|
1480 |
+ if cfg.Type == "none" { |
|
1481 |
+ return nil // do not start logging routines |
|
1482 |
+ } |
|
1483 |
+ |
|
1484 |
+ l, err := container.getLogger() |
|
1485 |
+ if err != nil { |
|
1486 |
+ return fmt.Errorf("Failed to initialize logging driver: %v", err) |
|
1483 | 1487 |
} |
1484 | 1488 |
|
1485 | 1489 |
copier, err := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) |
... | ... |
@@ -1490,6 +1480,11 @@ func (container *Container) startLogging() error { |
1490 | 1490 |
copier.Run() |
1491 | 1491 |
container.logDriver = l |
1492 | 1492 |
|
1493 |
+ // set LogPath field only for json-file logdriver |
|
1494 |
+ if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok { |
|
1495 |
+ container.LogPath = jl.LogPath() |
|
1496 |
+ } |
|
1497 |
+ |
|
1493 | 1498 |
return nil |
1494 | 1499 |
} |
1495 | 1500 |
|
... | ... |
@@ -1663,28 +1658,13 @@ func (c *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writ |
1663 | 1663 |
|
1664 | 1664 |
func (c *Container) AttachWithLogs(stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error { |
1665 | 1665 |
if logs { |
1666 |
- cLog, err := c.ReadLog("json") |
|
1667 |
- if err != nil && os.IsNotExist(err) { |
|
1668 |
- // Legacy logs |
|
1669 |
- logrus.Debugf("Old logs format") |
|
1670 |
- if stdout != nil { |
|
1671 |
- cLog, err := c.ReadLog("stdout") |
|
1672 |
- if err != nil { |
|
1673 |
- logrus.Errorf("Error reading logs (stdout): %s", err) |
|
1674 |
- } else if _, err := io.Copy(stdout, cLog); err != nil { |
|
1675 |
- logrus.Errorf("Error streaming logs (stdout): %s", err) |
|
1676 |
- } |
|
1677 |
- } |
|
1678 |
- if stderr != nil { |
|
1679 |
- cLog, err := c.ReadLog("stderr") |
|
1680 |
- if err != nil { |
|
1681 |
- logrus.Errorf("Error reading logs (stderr): %s", err) |
|
1682 |
- } else if _, err := io.Copy(stderr, cLog); err != nil { |
|
1683 |
- logrus.Errorf("Error streaming logs (stderr): %s", err) |
|
1684 |
- } |
|
1685 |
- } |
|
1686 |
- } else if err != nil { |
|
1687 |
- logrus.Errorf("Error reading logs (json): %s", err) |
|
1666 |
+ logDriver, err := c.getLogger() |
|
1667 |
+ cLog, err := logDriver.GetReader() |
|
1668 |
+ |
|
1669 |
+ if err != nil { |
|
1670 |
+ logrus.Errorf("Error reading logs: %s", err) |
|
1671 |
+ } else if c.LogDriverType() != jsonfilelog.Name { |
|
1672 |
+ logrus.Errorf("Reading logs not implemented for driver %s", c.LogDriverType()) |
|
1688 | 1673 |
} else { |
1689 | 1674 |
dec := json.NewDecoder(cLog) |
1690 | 1675 |
for { |
... | ... |
@@ -24,6 +24,7 @@ import ( |
24 | 24 |
"github.com/docker/docker/daemon/execdriver/execdrivers" |
25 | 25 |
"github.com/docker/docker/daemon/graphdriver" |
26 | 26 |
_ "github.com/docker/docker/daemon/graphdriver/vfs" |
27 |
+ "github.com/docker/docker/daemon/logger" |
|
27 | 28 |
"github.com/docker/docker/daemon/network" |
28 | 29 |
"github.com/docker/docker/daemon/networkdriver/bridge" |
29 | 30 |
"github.com/docker/docker/graph" |
... | ... |
@@ -798,6 +799,14 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo |
798 | 798 |
} |
799 | 799 |
}() |
800 | 800 |
|
801 |
+ // Verify logging driver type |
|
802 |
+ if config.LogConfig.Type != "none" { |
|
803 |
+ if _, err := logger.GetLogDriver(config.LogConfig.Type); err != nil { |
|
804 |
+ return nil, fmt.Errorf("error finding the logging driver: %v", err) |
|
805 |
+ } |
|
806 |
+ } |
|
807 |
+ logrus.Debugf("Using default logging driver %s", config.LogConfig.Type) |
|
808 |
+ |
|
801 | 809 |
if config.EnableSelinuxSupport { |
802 | 810 |
if selinuxEnabled() { |
803 | 811 |
// As Docker on btrfs and SELinux are incompatible at present, error on both being enabled |
804 | 812 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,9 @@ |
0 |
+package daemon |
|
1 |
+ |
|
2 |
+// Importing packages here only to make sure their init gets called and |
|
3 |
+// therefore they register themselves to the logdriver factory. |
|
4 |
+import ( |
|
5 |
+ _ "github.com/docker/docker/daemon/logger/journald" |
|
6 |
+ _ "github.com/docker/docker/daemon/logger/jsonfilelog" |
|
7 |
+ _ "github.com/docker/docker/daemon/logger/syslog" |
|
8 |
+) |
... | ... |
@@ -3,6 +3,7 @@ package logger |
3 | 3 |
import ( |
4 | 4 |
"bytes" |
5 | 5 |
"encoding/json" |
6 |
+ "errors" |
|
6 | 7 |
"io" |
7 | 8 |
"testing" |
8 | 9 |
"time" |
... | ... |
@@ -12,16 +13,14 @@ type TestLoggerJSON struct { |
12 | 12 |
*json.Encoder |
13 | 13 |
} |
14 | 14 |
|
15 |
-func (l *TestLoggerJSON) Log(m *Message) error { |
|
16 |
- return l.Encode(m) |
|
17 |
-} |
|
15 |
+func (l *TestLoggerJSON) Log(m *Message) error { return l.Encode(m) } |
|
18 | 16 |
|
19 |
-func (l *TestLoggerJSON) Close() error { |
|
20 |
- return nil |
|
21 |
-} |
|
17 |
+func (l *TestLoggerJSON) Close() error { return nil } |
|
18 |
+ |
|
19 |
+func (l *TestLoggerJSON) Name() string { return "json" } |
|
22 | 20 |
|
23 |
-func (l *TestLoggerJSON) Name() string { |
|
24 |
- return "json" |
|
21 |
+func (l *TestLoggerJSON) GetReader() (io.Reader, error) { |
|
22 |
+ return nil, errors.New("not used in the test") |
|
25 | 23 |
} |
26 | 24 |
|
27 | 25 |
type TestLoggerText struct { |
... | ... |
@@ -33,12 +32,12 @@ func (l *TestLoggerText) Log(m *Message) error { |
33 | 33 |
return err |
34 | 34 |
} |
35 | 35 |
|
36 |
-func (l *TestLoggerText) Close() error { |
|
37 |
- return nil |
|
38 |
-} |
|
36 |
+func (l *TestLoggerText) Close() error { return nil } |
|
37 |
+ |
|
38 |
+func (l *TestLoggerText) Name() string { return "text" } |
|
39 | 39 |
|
40 |
-func (l *TestLoggerText) Name() string { |
|
41 |
- return "text" |
|
40 |
+func (l *TestLoggerText) GetReader() (io.Reader, error) { |
|
41 |
+ return nil, errors.New("not used in the test") |
|
42 | 42 |
} |
43 | 43 |
|
44 | 44 |
func TestCopier(t *testing.T) { |
45 | 45 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,56 @@ |
0 |
+package logger |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "sync" |
|
5 |
+) |
|
6 |
+ |
|
7 |
+// Creator is a method that builds a logging driver instance with given context |
|
8 |
+type Creator func(Context) (Logger, error) |
|
9 |
+ |
|
10 |
+// Context provides enough information for a logging driver to do its function |
|
11 |
+type Context struct { |
|
12 |
+ ContainerID string |
|
13 |
+ ContainerName string |
|
14 |
+ LogPath string |
|
15 |
+} |
|
16 |
+ |
|
17 |
+type logdriverFactory struct { |
|
18 |
+ registry map[string]Creator |
|
19 |
+ m sync.Mutex |
|
20 |
+} |
|
21 |
+ |
|
22 |
+func (lf *logdriverFactory) register(name string, c Creator) error { |
|
23 |
+ lf.m.Lock() |
|
24 |
+ defer lf.m.Unlock() |
|
25 |
+ |
|
26 |
+ if _, ok := lf.registry[name]; ok { |
|
27 |
+ return fmt.Errorf("logger: log driver named '%s' is already registered", name) |
|
28 |
+ } |
|
29 |
+ lf.registry[name] = c |
|
30 |
+ return nil |
|
31 |
+} |
|
32 |
+ |
|
33 |
+func (lf *logdriverFactory) get(name string) (Creator, error) { |
|
34 |
+ lf.m.Lock() |
|
35 |
+ defer lf.m.Unlock() |
|
36 |
+ |
|
37 |
+ c, ok := lf.registry[name] |
|
38 |
+ if !ok { |
|
39 |
+ return c, fmt.Errorf("logger: no log driver named '%s' is registered", name) |
|
40 |
+ } |
|
41 |
+ return c, nil |
|
42 |
+} |
|
43 |
+ |
|
44 |
+var factory = &logdriverFactory{registry: make(map[string]Creator)} // global factory instance |
|
45 |
+ |
|
46 |
+// RegisterLogDriver registers the given logging driver builder with given logging |
|
47 |
+// driver name. |
|
48 |
+func RegisterLogDriver(name string, c Creator) error { |
|
49 |
+ return factory.register(name, c) |
|
50 |
+} |
|
51 |
+ |
|
52 |
+// GetLogDriver provides the logging driver builder for a logging driver name. |
|
53 |
+func GetLogDriver(name string) (Creator, error) { |
|
54 |
+ return factory.get(name) |
|
55 |
+} |
... | ... |
@@ -2,27 +2,38 @@ package journald |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 |
+ "io" |
|
5 | 6 |
|
7 |
+ "github.com/Sirupsen/logrus" |
|
6 | 8 |
"github.com/coreos/go-systemd/journal" |
7 | 9 |
"github.com/docker/docker/daemon/logger" |
8 | 10 |
) |
9 | 11 |
|
12 |
+const name = "journald" |
|
13 |
+ |
|
10 | 14 |
type Journald struct { |
11 | 15 |
Jmap map[string]string |
12 | 16 |
} |
13 | 17 |
|
14 |
-func New(id string, name string) (logger.Logger, error) { |
|
18 |
+func init() { |
|
19 |
+ if err := logger.RegisterLogDriver(name, New); err != nil { |
|
20 |
+ logrus.Fatal(err) |
|
21 |
+ } |
|
22 |
+} |
|
23 |
+ |
|
24 |
+func New(ctx logger.Context) (logger.Logger, error) { |
|
15 | 25 |
if !journal.Enabled() { |
16 | 26 |
return nil, fmt.Errorf("journald is not enabled on this host") |
17 | 27 |
} |
18 | 28 |
// Strip a leading slash so that people can search for |
19 | 29 |
// CONTAINER_NAME=foo rather than CONTAINER_NAME=/foo. |
30 |
+ name := ctx.ContainerName |
|
20 | 31 |
if name[0] == '/' { |
21 | 32 |
name = name[1:] |
22 | 33 |
} |
23 | 34 |
jmap := map[string]string{ |
24 |
- "CONTAINER_ID": id[:12], |
|
25 |
- "CONTAINER_ID_FULL": id, |
|
35 |
+ "CONTAINER_ID": ctx.ContainerID[:12], |
|
36 |
+ "CONTAINER_ID_FULL": ctx.ContainerID, |
|
26 | 37 |
"CONTAINER_NAME": name} |
27 | 38 |
return &Journald{Jmap: jmap}, nil |
28 | 39 |
} |
... | ... |
@@ -39,5 +50,9 @@ func (s *Journald) Close() error { |
39 | 39 |
} |
40 | 40 |
|
41 | 41 |
func (s *Journald) Name() string { |
42 |
- return "Journald" |
|
42 |
+ return name |
|
43 |
+} |
|
44 |
+ |
|
45 |
+func (s *Journald) GetReader() (io.Reader, error) { |
|
46 |
+ return nil, logger.ReadLogsNotSupported |
|
43 | 47 |
} |
... | ... |
@@ -2,31 +2,46 @@ package jsonfilelog |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"bytes" |
5 |
+ "io" |
|
5 | 6 |
"os" |
6 | 7 |
"sync" |
7 | 8 |
|
9 |
+ "github.com/Sirupsen/logrus" |
|
8 | 10 |
"github.com/docker/docker/daemon/logger" |
9 | 11 |
"github.com/docker/docker/pkg/jsonlog" |
10 | 12 |
"github.com/docker/docker/pkg/timeutils" |
11 | 13 |
) |
12 | 14 |
|
15 |
+const ( |
|
16 |
+ Name = "json-file" |
|
17 |
+) |
|
18 |
+ |
|
13 | 19 |
// JSONFileLogger is Logger implementation for default docker logging: |
14 | 20 |
// JSON objects to file |
15 | 21 |
type JSONFileLogger struct { |
16 | 22 |
buf *bytes.Buffer |
17 | 23 |
f *os.File // store for closing |
18 | 24 |
mu sync.Mutex // protects buffer |
25 |
+ |
|
26 |
+ ctx logger.Context |
|
27 |
+} |
|
28 |
+ |
|
29 |
+func init() { |
|
30 |
+ if err := logger.RegisterLogDriver(Name, New); err != nil { |
|
31 |
+ logrus.Fatal(err) |
|
32 |
+ } |
|
19 | 33 |
} |
20 | 34 |
|
21 | 35 |
// New creates new JSONFileLogger which writes to filename |
22 |
-func New(filename string) (logger.Logger, error) { |
|
23 |
- log, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) |
|
36 |
+func New(ctx logger.Context) (logger.Logger, error) { |
|
37 |
+ log, err := os.OpenFile(ctx.LogPath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) |
|
24 | 38 |
if err != nil { |
25 | 39 |
return nil, err |
26 | 40 |
} |
27 | 41 |
return &JSONFileLogger{ |
28 | 42 |
f: log, |
29 | 43 |
buf: bytes.NewBuffer(nil), |
44 |
+ ctx: ctx, |
|
30 | 45 |
}, nil |
31 | 46 |
} |
32 | 47 |
|
... | ... |
@@ -34,6 +49,7 @@ func New(filename string) (logger.Logger, error) { |
34 | 34 |
func (l *JSONFileLogger) Log(msg *logger.Message) error { |
35 | 35 |
l.mu.Lock() |
36 | 36 |
defer l.mu.Unlock() |
37 |
+ |
|
37 | 38 |
timestamp, err := timeutils.FastMarshalJSON(msg.Timestamp) |
38 | 39 |
if err != nil { |
39 | 40 |
return err |
... | ... |
@@ -52,6 +68,14 @@ func (l *JSONFileLogger) Log(msg *logger.Message) error { |
52 | 52 |
return nil |
53 | 53 |
} |
54 | 54 |
|
55 |
+func (l *JSONFileLogger) GetReader() (io.Reader, error) { |
|
56 |
+ return os.Open(l.ctx.LogPath) |
|
57 |
+} |
|
58 |
+ |
|
59 |
+func (l *JSONFileLogger) LogPath() string { |
|
60 |
+ return l.ctx.LogPath |
|
61 |
+} |
|
62 |
+ |
|
55 | 63 |
// Close closes underlying file |
56 | 64 |
func (l *JSONFileLogger) Close() error { |
57 | 65 |
return l.f.Close() |
... | ... |
@@ -59,5 +83,5 @@ func (l *JSONFileLogger) Close() error { |
59 | 59 |
|
60 | 60 |
// Name returns name of this logger |
61 | 61 |
func (l *JSONFileLogger) Name() string { |
62 |
- return "JSONFile" |
|
62 |
+ return Name |
|
63 | 63 |
} |
... | ... |
@@ -12,18 +12,22 @@ import ( |
12 | 12 |
) |
13 | 13 |
|
14 | 14 |
func TestJSONFileLogger(t *testing.T) { |
15 |
+ cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" |
|
15 | 16 |
tmp, err := ioutil.TempDir("", "docker-logger-") |
16 | 17 |
if err != nil { |
17 | 18 |
t.Fatal(err) |
18 | 19 |
} |
19 | 20 |
defer os.RemoveAll(tmp) |
20 | 21 |
filename := filepath.Join(tmp, "container.log") |
21 |
- l, err := New(filename) |
|
22 |
+ l, err := New(logger.Context{ |
|
23 |
+ ContainerID: cid, |
|
24 |
+ LogPath: filename, |
|
25 |
+ }) |
|
22 | 26 |
if err != nil { |
23 | 27 |
t.Fatal(err) |
24 | 28 |
} |
25 | 29 |
defer l.Close() |
26 |
- cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" |
|
30 |
+ |
|
27 | 31 |
if err := l.Log(&logger.Message{ContainerID: cid, Line: []byte("line1"), Source: "src1"}); err != nil { |
28 | 32 |
t.Fatal(err) |
29 | 33 |
} |
... | ... |
@@ -48,18 +52,22 @@ func TestJSONFileLogger(t *testing.T) { |
48 | 48 |
} |
49 | 49 |
|
50 | 50 |
func BenchmarkJSONFileLogger(b *testing.B) { |
51 |
+ cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" |
|
51 | 52 |
tmp, err := ioutil.TempDir("", "docker-logger-") |
52 | 53 |
if err != nil { |
53 | 54 |
b.Fatal(err) |
54 | 55 |
} |
55 | 56 |
defer os.RemoveAll(tmp) |
56 | 57 |
filename := filepath.Join(tmp, "container.log") |
57 |
- l, err := New(filename) |
|
58 |
+ l, err := New(logger.Context{ |
|
59 |
+ ContainerID: cid, |
|
60 |
+ LogPath: filename, |
|
61 |
+ }) |
|
58 | 62 |
if err != nil { |
59 | 63 |
b.Fatal(err) |
60 | 64 |
} |
61 | 65 |
defer l.Close() |
62 |
- cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" |
|
66 |
+ |
|
63 | 67 |
testLine := "Line that thinks that it is log line from docker\n" |
64 | 68 |
msg := &logger.Message{ContainerID: cid, Line: []byte(testLine), Source: "stderr", Timestamp: time.Now().UTC()} |
65 | 69 |
jsonlog, err := (&jsonlog.JSONLog{Log: string(msg.Line) + "\n", Stream: msg.Source, Created: msg.Timestamp}).MarshalJSON() |
... | ... |
@@ -1,6 +1,12 @@ |
1 | 1 |
package logger |
2 | 2 |
|
3 |
-import "time" |
|
3 |
+import ( |
|
4 |
+ "errors" |
|
5 |
+ "io" |
|
6 |
+ "time" |
|
7 |
+) |
|
8 |
+ |
|
9 |
+var ReadLogsNotSupported = errors.New("configured logging reader does not support reading") |
|
4 | 10 |
|
5 | 11 |
// Message is datastructure that represents record from some container |
6 | 12 |
type Message struct { |
... | ... |
@@ -15,4 +21,5 @@ type Logger interface { |
15 | 15 |
Log(*Message) error |
16 | 16 |
Name() string |
17 | 17 |
Close() error |
18 |
+ GetReader() (io.Reader, error) |
|
18 | 19 |
} |
... | ... |
@@ -2,22 +2,34 @@ package syslog |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 |
+ "io" |
|
5 | 6 |
"log/syslog" |
6 | 7 |
"os" |
7 | 8 |
"path" |
8 | 9 |
|
10 |
+ "github.com/Sirupsen/logrus" |
|
9 | 11 |
"github.com/docker/docker/daemon/logger" |
10 | 12 |
) |
11 | 13 |
|
14 |
+const name = "syslog" |
|
15 |
+ |
|
12 | 16 |
type Syslog struct { |
13 | 17 |
writer *syslog.Writer |
14 | 18 |
} |
15 | 19 |
|
16 |
-func New(tag string) (logger.Logger, error) { |
|
20 |
+func init() { |
|
21 |
+ if err := logger.RegisterLogDriver(name, New); err != nil { |
|
22 |
+ logrus.Fatal(err) |
|
23 |
+ } |
|
24 |
+} |
|
25 |
+ |
|
26 |
+func New(ctx logger.Context) (logger.Logger, error) { |
|
27 |
+ tag := ctx.ContainerID[:12] |
|
17 | 28 |
log, err := syslog.New(syslog.LOG_DAEMON, fmt.Sprintf("%s/%s", path.Base(os.Args[0]), tag)) |
18 | 29 |
if err != nil { |
19 | 30 |
return nil, err |
20 | 31 |
} |
32 |
+ |
|
21 | 33 |
return &Syslog{ |
22 | 34 |
writer: log, |
23 | 35 |
}, nil |
... | ... |
@@ -35,5 +47,9 @@ func (s *Syslog) Close() error { |
35 | 35 |
} |
36 | 36 |
|
37 | 37 |
func (s *Syslog) Name() string { |
38 |
- return "Syslog" |
|
38 |
+ return name |
|
39 |
+} |
|
40 |
+ |
|
41 |
+func (s *Syslog) GetReader() (io.Reader, error) { |
|
42 |
+ return nil, logger.ReadLogsNotSupported |
|
39 | 43 |
} |
... | ... |
@@ -11,6 +11,7 @@ import ( |
11 | 11 |
"time" |
12 | 12 |
|
13 | 13 |
"github.com/Sirupsen/logrus" |
14 |
+ "github.com/docker/docker/daemon/logger/jsonfilelog" |
|
14 | 15 |
"github.com/docker/docker/pkg/jsonlog" |
15 | 16 |
"github.com/docker/docker/pkg/stdcopy" |
16 | 17 |
"github.com/docker/docker/pkg/tailfile" |
... | ... |
@@ -56,32 +57,15 @@ func (daemon *Daemon) ContainerLogs(name string, config *ContainerLogsConfig) er |
56 | 56 |
errStream = outStream |
57 | 57 |
} |
58 | 58 |
|
59 |
- if container.LogDriverType() != "json-file" { |
|
59 |
+ if container.LogDriverType() != jsonfilelog.Name { |
|
60 | 60 |
return fmt.Errorf("\"logs\" endpoint is supported only for \"json-file\" logging driver") |
61 | 61 |
} |
62 |
- cLog, err := container.ReadLog("json") |
|
63 |
- if err != nil && os.IsNotExist(err) { |
|
64 |
- // Legacy logs |
|
65 |
- logrus.Debugf("Old logs format") |
|
66 |
- if config.UseStdout { |
|
67 |
- cLog, err := container.ReadLog("stdout") |
|
68 |
- if err != nil { |
|
69 |
- logrus.Errorf("Error reading logs (stdout): %s", err) |
|
70 |
- } else if _, err := io.Copy(outStream, cLog); err != nil { |
|
71 |
- logrus.Errorf("Error streaming logs (stdout): %s", err) |
|
72 |
- } |
|
73 |
- } |
|
74 |
- if config.UseStderr { |
|
75 |
- cLog, err := container.ReadLog("stderr") |
|
76 |
- if err != nil { |
|
77 |
- logrus.Errorf("Error reading logs (stderr): %s", err) |
|
78 |
- } else if _, err := io.Copy(errStream, cLog); err != nil { |
|
79 |
- logrus.Errorf("Error streaming logs (stderr): %s", err) |
|
80 |
- } |
|
81 |
- } |
|
82 |
- } else if err != nil { |
|
83 |
- logrus.Errorf("Error reading logs (json): %s", err) |
|
62 |
+ logDriver, err := container.getLogger() |
|
63 |
+ cLog, err := logDriver.GetReader() |
|
64 |
+ if err != nil { |
|
65 |
+ logrus.Errorf("Error reading logs: %s", err) |
|
84 | 66 |
} else { |
67 |
+ // json-file driver |
|
85 | 68 |
if config.Tail != "all" { |
86 | 69 |
var err error |
87 | 70 |
lines, err = strconv.Atoi(config.Tail) |