github.com/mssola/docker@v1.8.1/daemon/logger/fluentd/fluentd.go (about)

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