github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/daemon/logger/gelf/gelf.go (about)

     1  // +build linux
     2  
     3  // Package gelf provides the log driver for forwarding server logs to
     4  // endpoints that support the Graylog Extended Log Format.
     5  package gelf
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"net"
    11  	"net/url"
    12  	"time"
    13  
    14  	"github.com/Graylog2/go-gelf/gelf"
    15  	"github.com/Sirupsen/logrus"
    16  	"github.com/docker/docker/daemon/logger"
    17  	"github.com/docker/docker/daemon/logger/loggerutils"
    18  	"github.com/docker/docker/pkg/urlutil"
    19  )
    20  
    21  const name = "gelf"
    22  
    23  type gelfLogger struct {
    24  	writer   *gelf.Writer
    25  	ctx      logger.Context
    26  	hostname string
    27  	extra    map[string]interface{}
    28  }
    29  
    30  func init() {
    31  	if err := logger.RegisterLogDriver(name, New); err != nil {
    32  		logrus.Fatal(err)
    33  	}
    34  	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
    35  		logrus.Fatal(err)
    36  	}
    37  }
    38  
    39  // New creates a gelf logger using the configuration passed in on the
    40  // context. Supported context configuration variables are
    41  // gelf-address, & gelf-tag.
    42  func New(ctx logger.Context) (logger.Logger, error) {
    43  	// parse gelf address
    44  	address, err := parseAddress(ctx.Config["gelf-address"])
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	// collect extra data for GELF message
    50  	hostname, err := ctx.Hostname()
    51  	if err != nil {
    52  		return nil, fmt.Errorf("gelf: cannot access hostname to set source field")
    53  	}
    54  
    55  	// remove trailing slash from container name
    56  	containerName := bytes.TrimLeft([]byte(ctx.ContainerName), "/")
    57  
    58  	// parse log tag
    59  	tag, err := loggerutils.ParseLogTag(ctx, "")
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	extra := map[string]interface{}{
    65  		"_container_id":   ctx.ContainerID,
    66  		"_container_name": string(containerName),
    67  		"_image_id":       ctx.ContainerImageID,
    68  		"_image_name":     ctx.ContainerImageName,
    69  		"_command":        ctx.Command(),
    70  		"_tag":            tag,
    71  		"_created":        ctx.ContainerCreated,
    72  	}
    73  
    74  	extraAttrs := ctx.ExtraAttributes(func(key string) string {
    75  		if key[0] == '_' {
    76  			return key
    77  		}
    78  		return "_" + key
    79  	})
    80  	for k, v := range extraAttrs {
    81  		extra[k] = v
    82  	}
    83  
    84  	// create new gelfWriter
    85  	gelfWriter, err := gelf.NewWriter(address)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err)
    88  	}
    89  
    90  	return &gelfLogger{
    91  		writer:   gelfWriter,
    92  		ctx:      ctx,
    93  		hostname: hostname,
    94  		extra:    extra,
    95  	}, nil
    96  }
    97  
    98  func (s *gelfLogger) Log(msg *logger.Message) error {
    99  	level := gelf.LOG_INFO
   100  	if msg.Source == "stderr" {
   101  		level = gelf.LOG_ERR
   102  	}
   103  
   104  	m := gelf.Message{
   105  		Version:  "1.1",
   106  		Host:     s.hostname,
   107  		Short:    string(msg.Line),
   108  		TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0,
   109  		Level:    level,
   110  		Extra:    s.extra,
   111  	}
   112  
   113  	if err := s.writer.WriteMessage(&m); err != nil {
   114  		return fmt.Errorf("gelf: cannot send GELF message: %v", err)
   115  	}
   116  	return nil
   117  }
   118  
   119  func (s *gelfLogger) Close() error {
   120  	return s.writer.Close()
   121  }
   122  
   123  func (s *gelfLogger) Name() string {
   124  	return name
   125  }
   126  
   127  // ValidateLogOpt looks for gelf specific log options gelf-address, &
   128  // gelf-tag.
   129  func ValidateLogOpt(cfg map[string]string) error {
   130  	for key := range cfg {
   131  		switch key {
   132  		case "gelf-address":
   133  		case "gelf-tag":
   134  		case "tag":
   135  		case "labels":
   136  		case "env":
   137  		default:
   138  			return fmt.Errorf("unknown log opt '%s' for gelf log driver", key)
   139  		}
   140  	}
   141  
   142  	if _, err := parseAddress(cfg["gelf-address"]); err != nil {
   143  		return err
   144  	}
   145  
   146  	return nil
   147  }
   148  
   149  func parseAddress(address string) (string, error) {
   150  	if address == "" {
   151  		return "", nil
   152  	}
   153  	if !urlutil.IsTransportURL(address) {
   154  		return "", fmt.Errorf("gelf-address should be in form proto://address, got %v", address)
   155  	}
   156  	url, err := url.Parse(address)
   157  	if err != nil {
   158  		return "", err
   159  	}
   160  
   161  	// we support only udp
   162  	if url.Scheme != "udp" {
   163  		return "", fmt.Errorf("gelf: endpoint needs to be UDP")
   164  	}
   165  
   166  	// get host and port
   167  	if _, _, err = net.SplitHostPort(url.Host); err != nil {
   168  		return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port")
   169  	}
   170  
   171  	return url.Host, nil
   172  }