github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/daemon/logger/fluentd/fluentd.go (about)

     1  // Package fluentd provides the log driver for forwarding server logs
     2  // to fluentd endpoints.
     3  package fluentd
     4  
     5  import (
     6  	"fmt"
     7  	"math"
     8  	"net"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/docker/daemon/logger"
    14  	"github.com/docker/docker/daemon/logger/loggerutils"
    15  	"github.com/fluent/fluent-logger-golang/fluent"
    16  )
    17  
    18  type fluentd struct {
    19  	tag           string
    20  	containerID   string
    21  	containerName string
    22  	writer        *fluent.Fluent
    23  }
    24  
    25  const (
    26  	name             = "fluentd"
    27  	defaultHostName  = "localhost"
    28  	defaultPort      = 24224
    29  	defaultTagPrefix = "docker"
    30  )
    31  
    32  func init() {
    33  	if err := logger.RegisterLogDriver(name, New); err != nil {
    34  		logrus.Fatal(err)
    35  	}
    36  	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
    37  		logrus.Fatal(err)
    38  	}
    39  }
    40  
    41  // New creates a fluentd logger using the configuration passed in on
    42  // the context. Supported context configuration variables are
    43  // fluentd-address & fluentd-tag.
    44  func New(ctx logger.Context) (logger.Logger, error) {
    45  	host, port, err := parseAddress(ctx.Config["fluentd-address"])
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}")
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s.", ctx.ContainerID, host, port, tag)
    56  
    57  	// logger tries to recoonect 2**32 - 1 times
    58  	// failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds]
    59  	log, err := fluent.New(fluent.Config{FluentPort: port, FluentHost: host, RetryWait: 1000, MaxRetry: math.MaxInt32})
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	return &fluentd{
    64  		tag:           tag,
    65  		containerID:   ctx.ContainerID,
    66  		containerName: ctx.ContainerName,
    67  		writer:        log,
    68  	}, nil
    69  }
    70  
    71  func (f *fluentd) Log(msg *logger.Message) error {
    72  	data := map[string]string{
    73  		"container_id":   f.containerID,
    74  		"container_name": f.containerName,
    75  		"source":         msg.Source,
    76  		"log":            string(msg.Line),
    77  	}
    78  	// fluent-logger-golang buffers logs from failures and disconnections,
    79  	// and these are transferred again automatically.
    80  	return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
    81  }
    82  
    83  func (f *fluentd) Close() error {
    84  	return f.writer.Close()
    85  }
    86  
    87  func (f *fluentd) Name() string {
    88  	return name
    89  }
    90  
    91  // ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag.
    92  func ValidateLogOpt(cfg map[string]string) error {
    93  	for key := range cfg {
    94  		switch key {
    95  		case "fluentd-address":
    96  		case "fluentd-tag":
    97  		case "tag":
    98  		default:
    99  			return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
   100  		}
   101  	}
   102  
   103  	if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil {
   104  		return err
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func parseAddress(address string) (string, int, error) {
   111  	if address == "" {
   112  		return defaultHostName, defaultPort, nil
   113  	}
   114  
   115  	host, port, err := net.SplitHostPort(address)
   116  	if err != nil {
   117  		if !strings.Contains(err.Error(), "missing port in address") {
   118  			return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
   119  		}
   120  		return host, defaultPort, nil
   121  	}
   122  
   123  	portnum, err := strconv.Atoi(port)
   124  	if err != nil {
   125  		return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
   126  	}
   127  	return host, portnum, nil
   128  }