Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rollover log driver, and --log-driver-opts flag #11485

Merged
merged 1 commit into from
Jul 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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