github.com/tompao/docker@v1.9.1/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  	// remove trailing and leading whitespace
   100  	short := bytes.TrimSpace([]byte(msg.Line))
   101  
   102  	level := gelf.LOG_INFO
   103  	if msg.Source == "stderr" {
   104  		level = gelf.LOG_ERR
   105  	}
   106  
   107  	m := gelf.Message{
   108  		Version:  "1.1",
   109  		Host:     s.hostname,
   110  		Short:    string(short),
   111  		TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0,
   112  		Level:    level,
   113  		Extra:    s.extra,
   114  	}
   115  
   116  	if err := s.writer.WriteMessage(&m); err != nil {
   117  		return fmt.Errorf("gelf: cannot send GELF message: %v", err)
   118  	}
   119  	return nil
   120  }
   121  
   122  func (s *gelfLogger) Close() error {
   123  	return s.writer.Close()
   124  }
   125  
   126  func (s *gelfLogger) Name() string {
   127  	return name
   128  }
   129  
   130  // ValidateLogOpt looks for gelf specific log options gelf-address, &
   131  // gelf-tag.
   132  func ValidateLogOpt(cfg map[string]string) error {
   133  	for key := range cfg {
   134  		switch key {
   135  		case "gelf-address":
   136  		case "gelf-tag":
   137  		case "tag":
   138  		case "labels":
   139  		case "env":
   140  		default:
   141  			return fmt.Errorf("unknown log opt '%s' for gelf log driver", key)
   142  		}
   143  	}
   144  
   145  	if _, err := parseAddress(cfg["gelf-address"]); err != nil {
   146  		return err
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  func parseAddress(address string) (string, error) {
   153  	if address == "" {
   154  		return "", nil
   155  	}
   156  	if !urlutil.IsTransportURL(address) {
   157  		return "", fmt.Errorf("gelf-address should be in form proto://address, got %v", address)
   158  	}
   159  	url, err := url.Parse(address)
   160  	if err != nil {
   161  		return "", err
   162  	}
   163  
   164  	// we support only udp
   165  	if url.Scheme != "udp" {
   166  		return "", fmt.Errorf("gelf: endpoint needs to be UDP")
   167  	}
   168  
   169  	// get host and port
   170  	if _, _, err = net.SplitHostPort(url.Host); err != nil {
   171  		return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port")
   172  	}
   173  
   174  	return url.Host, nil
   175  }