Skip to content

Commit

Permalink
Merge pull request moby#11485 from wlan0/rollover_log
Browse files Browse the repository at this point in the history
Add rollover log driver, and --log-driver-opts flag
  • Loading branch information
thaJeztah committed Jul 17, 2015
2 parents a192105 + 9b782d3 commit 415f744
Show file tree
Hide file tree
Showing 16 changed files with 427 additions and 104 deletions.
2 changes: 1 addition & 1 deletion api/client/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
since := cmd.String([]string{"-since"}, "", "Show logs since timestamp")
times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
tail := cmd.String([]string{"-tail"}, "all", "Number of lines to show from the end of the logs")
tail := cmd.String([]string{"-tail"}, "latest", "Number of lines to show from the end of the logs")
cmd.Require(flag.Exact, 1)

cmd.ParseFlags(args, true)
Expand Down
52 changes: 31 additions & 21 deletions daemon/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,10 @@ func (container *Container) SetHostConfig(hostConfig *runconfig.HostConfig) {

func (container *Container) getLogConfig() runconfig.LogConfig {
cfg := container.hostConfig.LogConfig
if cfg.Type != "" { // container has log driver configured
if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured
if cfg.Type == "" {
cfg.Type = jsonfilelog.Name
}
return cfg
}
// Use daemon's default log config for containers
Expand All @@ -719,6 +722,9 @@ func (container *Container) getLogConfig() runconfig.LogConfig {

func (container *Container) getLogger() (logger.Logger, error) {
cfg := container.getLogConfig()
if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
return nil, err
}
c, err := logger.GetLogDriver(cfg.Type)
if err != nil {
return nil, fmt.Errorf("Failed to get logging factory: %v", err)
Expand Down Expand Up @@ -891,28 +897,32 @@ func (c *Container) AttachWithLogs(stdin io.ReadCloser, stdout, stderr io.Writer

if logs {
logDriver, err := c.getLogger()
cLog, err := logDriver.GetReader()

if err != nil {
logrus.Errorf("Error reading logs: %s", err)
} else if c.LogDriverType() != jsonfilelog.Name {
logrus.Errorf("Reading logs not implemented for driver %s", c.LogDriverType())
logrus.Errorf("Error obtaining the logger %v", err)
return err
}
if _, ok := logDriver.(logger.Reader); !ok {
logrus.Errorf("cannot read logs for [%s] driver", logDriver.Name())
} else {
dec := json.NewDecoder(cLog)
for {
l := &jsonlog.JSONLog{}

if err := dec.Decode(l); err == io.EOF {
break
} else if err != nil {
logrus.Errorf("Error streaming logs: %s", err)
break
}
if l.Stream == "stdout" && stdout != nil {
io.WriteString(stdout, l.Log)
}
if l.Stream == "stderr" && stderr != nil {
io.WriteString(stderr, l.Log)
if cLog, err := logDriver.(logger.Reader).ReadLog(); err != nil {
logrus.Errorf("Error reading logs %v", err)
} else {
dec := json.NewDecoder(cLog)
for {
l := &jsonlog.JSONLog{}

if err := dec.Decode(l); err == io.EOF {
break
} else if err != nil {
logrus.Errorf("Error streaming logs: %s", err)
break
}
if l.Stream == "stdout" && stdout != nil {
io.WriteString(stdout, l.Log)
}
if l.Stream == "stderr" && stderr != nil {
io.WriteString(stderr, l.Log)
}
}
}
}
Expand Down
9 changes: 0 additions & 9 deletions daemon/logger/copier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package logger
import (
"bytes"
"encoding/json"
"errors"
"io"
"testing"
"time"
Expand All @@ -19,10 +18,6 @@ func (l *TestLoggerJSON) Close() error { return nil }

func (l *TestLoggerJSON) Name() string { return "json" }

func (l *TestLoggerJSON) GetReader() (io.Reader, error) {
return nil, errors.New("not used in the test")
}

type TestLoggerText struct {
*bytes.Buffer
}
Expand All @@ -36,10 +31,6 @@ func (l *TestLoggerText) Close() error { return nil }

func (l *TestLoggerText) Name() string { return "text" }

func (l *TestLoggerText) GetReader() (io.Reader, error) {
return nil, errors.New("not used in the test")
}

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"
Expand Down
41 changes: 38 additions & 3 deletions daemon/logger/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import (
// Creator is a method that builds a logging driver instance with given context
type Creator func(Context) (Logger, error)

//LogOptValidator is a method that validates the log opts provided
type LogOptValidator func(cfg map[string]string) error

// Context provides enough information for a logging driver to do its function
type Context struct {
Config map[string]string
Expand Down Expand Up @@ -42,8 +45,9 @@ func (ctx *Context) Command() string {
}

type logdriverFactory struct {
registry map[string]Creator
m sync.Mutex
registry map[string]Creator
optValidator map[string]LogOptValidator
m sync.Mutex
}

func (lf *logdriverFactory) register(name string, c Creator) error {
Expand All @@ -57,6 +61,17 @@ func (lf *logdriverFactory) register(name string, c Creator) error {
return nil
}

func (lf *logdriverFactory) registerLogOptValidator(name string, l LogOptValidator) error {
lf.m.Lock()
defer lf.m.Unlock()

if _, ok := lf.optValidator[name]; ok {
return fmt.Errorf("logger: log driver named '%s' is already registered", name)
}
lf.optValidator[name] = l
return nil
}

func (lf *logdriverFactory) get(name string) (Creator, error) {
lf.m.Lock()
defer lf.m.Unlock()
Expand All @@ -68,15 +83,35 @@ func (lf *logdriverFactory) get(name string) (Creator, error) {
return c, nil
}

var factory = &logdriverFactory{registry: make(map[string]Creator)} // global factory instance
func (lf *logdriverFactory) getLogOptValidator(name string) LogOptValidator {
lf.m.Lock()
defer lf.m.Unlock()

c, _ := lf.optValidator[name]
return c
}

var factory = &logdriverFactory{registry: make(map[string]Creator), optValidator: make(map[string]LogOptValidator)} // global factory instance

// RegisterLogDriver registers the given logging driver builder with given logging
// driver name.
func RegisterLogDriver(name string, c Creator) error {
return factory.register(name, c)
}

func RegisterLogOptValidator(name string, l LogOptValidator) error {
return factory.registerLogOptValidator(name, l)
}

// GetLogDriver provides the logging driver builder for a logging driver name.
func GetLogDriver(name string) (Creator, error) {
return factory.get(name)
}

func ValidateLogOpts(name string, cfg map[string]string) error {
l := factory.getLogOptValidator(name)
if l != nil {
return l(cfg)
}
return fmt.Errorf("Log Opts are not valid for [%s] driver", name)
}
21 changes: 16 additions & 5 deletions daemon/logger/fluentd/fluentd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package fluentd

import (
"bytes"
"io"
"fmt"
"math"
"net"
"strconv"
Expand Down Expand Up @@ -38,6 +38,9 @@ func init() {
if err := logger.RegisterLogDriver(name, New); err != nil {
logrus.Fatal(err)
}
if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
logrus.Fatal(err)
}
}

func parseConfig(ctx logger.Context) (string, int, string, error) {
Expand Down Expand Up @@ -116,14 +119,22 @@ func (f *Fluentd) Log(msg *logger.Message) error {
return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
}

func ValidateLogOpt(cfg map[string]string) error {
for key := range cfg {
switch key {
case "fluentd-address":
case "fluentd-tag":
default:
return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
}
}
return nil
}

func (f *Fluentd) Close() error {
return f.writer.Close()
}

func (f *Fluentd) Name() string {
return name
}

func (s *Fluentd) GetReader() (io.Reader, error) {
return nil, logger.ReadLogsNotSupported
}
20 changes: 15 additions & 5 deletions daemon/logger/gelf/gelf.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package gelf
import (
"bytes"
"fmt"
"io"
"net"
"net/url"
"time"
Expand Down Expand Up @@ -39,6 +38,9 @@ func init() {
if err := logger.RegisterLogDriver(name, New); err != nil {
logrus.Fatal(err)
}
if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
logrus.Fatal(err)
}
}

func New(ctx logger.Context) (logger.Logger, error) {
Expand Down Expand Up @@ -113,10 +115,6 @@ func (s *GelfLogger) Log(msg *logger.Message) error {
return nil
}

func (s *GelfLogger) GetReader() (io.Reader, error) {
return nil, logger.ReadLogsNotSupported
}

func (s *GelfLogger) Close() error {
return s.writer.Close()
}
Expand All @@ -125,6 +123,18 @@ func (s *GelfLogger) Name() string {
return name
}

func ValidateLogOpt(cfg map[string]string) error {
for key := range cfg {
switch key {
case "gelf-address":
case "gelf-tag":
default:
return fmt.Errorf("unknown log opt '%s' for gelf log driver", key)
}
}
return nil
}

func parseAddress(address string) (string, error) {
if urlutil.IsTransportURL(address) {
url, err := url.Parse(address)
Expand Down
5 changes: 0 additions & 5 deletions daemon/logger/journald/journald.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package journald

import (
"fmt"
"io"

"github.com/Sirupsen/logrus"
"github.com/coreos/go-systemd/journal"
Expand Down Expand Up @@ -54,7 +53,3 @@ func (s *Journald) Close() error {
func (s *Journald) Name() string {
return name
}

func (s *Journald) GetReader() (io.Reader, error) {
return nil, logger.ReadLogsNotSupported
}
Loading

0 comments on commit 415f744

Please sign in to comment.