github.com/clintkitson/docker@v1.9.1/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  	extra         map[string]string
    24  }
    25  
    26  const (
    27  	name             = "fluentd"
    28  	defaultHostName  = "localhost"
    29  	defaultPort      = 24224
    30  	defaultTagPrefix = "docker"
    31  )
    32  
    33  func init() {
    34  	if err := logger.RegisterLogDriver(name, New); err != nil {
    35  		logrus.Fatal(err)
    36  	}
    37  	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
    38  		logrus.Fatal(err)
    39  	}
    40  }
    41  
    42  // New creates a fluentd logger using the configuration passed in on
    43  // the context. Supported context configuration variables are
    44  // fluentd-address & fluentd-tag.
    45  func New(ctx logger.Context) (logger.Logger, error) {
    46  	host, port, err := parseAddress(ctx.Config["fluentd-address"])
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}")
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	extra := ctx.ExtraAttributes(nil)
    56  	logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s, extra:%v.", ctx.ContainerID, host, port, tag, extra)
    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  		extra:         extra,
    69  	}, nil
    70  }
    71  
    72  func (f *fluentd) Log(msg *logger.Message) error {
    73  	data := map[string]string{
    74  		"container_id":   f.containerID,
    75  		"container_name": f.containerName,
    76  		"source":         msg.Source,
    77  		"log":            string(msg.Line),
    78  	}
    79  	for k, v := range f.extra {
    80  		data[k] = v
    81  	}
    82  	// fluent-logger-golang buffers logs from failures and disconnections,
    83  	// and these are transferred again automatically.
    84  	return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
    85  }
    86  
    87  func (f *fluentd) Close() error {
    88  	return f.writer.Close()
    89  }
    90  
    91  func (f *fluentd) Name() string {
    92  	return name
    93  }
    94  
    95  // ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag.
    96  func ValidateLogOpt(cfg map[string]string) error {
    97  	for key := range cfg {
    98  		switch key {
    99  		case "fluentd-address":
   100  		case "fluentd-tag":
   101  		case "tag":
   102  		case "labels":
   103  		case "env":
   104  		default:
   105  			return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
   106  		}
   107  	}
   108  
   109  	if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil {
   110  		return err
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func parseAddress(address string) (string, int, error) {
   117  	if address == "" {
   118  		return defaultHostName, defaultPort, nil
   119  	}
   120  
   121  	host, port, err := net.SplitHostPort(address)
   122  	if err != nil {
   123  		if !strings.Contains(err.Error(), "missing port in address") {
   124  			return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
   125  		}
   126  		return host, defaultPort, nil
   127  	}
   128  
   129  	portnum, err := strconv.Atoi(port)
   130  	if err != nil {
   131  		return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
   132  	}
   133  	return host, portnum, nil
   134  }