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