github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/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/pkg/urlutil"
    18  )
    19  
    20  const name = "gelf"
    21  
    22  type gelfLogger struct {
    23  	writer *gelf.Writer
    24  	ctx    logger.Context
    25  	fields gelfFields
    26  }
    27  
    28  type gelfFields struct {
    29  	hostname      string
    30  	containerID   string
    31  	containerName string
    32  	imageID       string
    33  	imageName     string
    34  	command       string
    35  	tag           string
    36  	created       time.Time
    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  // New creates a gelf logger using the configuration passed in on the
    49  // context. Supported context configuration variables are
    50  // gelf-address, & gelf-tag.
    51  func New(ctx logger.Context) (logger.Logger, error) {
    52  	// parse gelf address
    53  	address, err := parseAddress(ctx.Config["gelf-address"])
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	// collect extra data for GELF message
    59  	hostname, err := ctx.Hostname()
    60  	if err != nil {
    61  		return nil, fmt.Errorf("gelf: cannot access hostname to set source field")
    62  	}
    63  
    64  	// remove trailing slash from container name
    65  	containerName := bytes.TrimLeft([]byte(ctx.ContainerName), "/")
    66  
    67  	fields := gelfFields{
    68  		hostname:      hostname,
    69  		containerID:   ctx.ContainerID,
    70  		containerName: string(containerName),
    71  		imageID:       ctx.ContainerImageID,
    72  		imageName:     ctx.ContainerImageName,
    73  		command:       ctx.Command(),
    74  		tag:           ctx.Config["gelf-tag"],
    75  		created:       ctx.ContainerCreated,
    76  	}
    77  
    78  	// create new gelfWriter
    79  	gelfWriter, err := gelf.NewWriter(address)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err)
    82  	}
    83  
    84  	return &gelfLogger{
    85  		writer: gelfWriter,
    86  		ctx:    ctx,
    87  		fields: fields,
    88  	}, nil
    89  }
    90  
    91  func (s *gelfLogger) Log(msg *logger.Message) error {
    92  	// remove trailing and leading whitespace
    93  	short := bytes.TrimSpace([]byte(msg.Line))
    94  
    95  	level := gelf.LOG_INFO
    96  	if msg.Source == "stderr" {
    97  		level = gelf.LOG_ERR
    98  	}
    99  
   100  	m := gelf.Message{
   101  		Version:  "1.1",
   102  		Host:     s.fields.hostname,
   103  		Short:    string(short),
   104  		TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0,
   105  		Level:    level,
   106  		Extra: map[string]interface{}{
   107  			"_container_id":   s.fields.containerID,
   108  			"_container_name": s.fields.containerName,
   109  			"_image_id":       s.fields.imageID,
   110  			"_image_name":     s.fields.imageName,
   111  			"_command":        s.fields.command,
   112  			"_tag":            s.fields.tag,
   113  			"_created":        s.fields.created,
   114  		},
   115  	}
   116  
   117  	if err := s.writer.WriteMessage(&m); err != nil {
   118  		return fmt.Errorf("gelf: cannot send GELF message: %v", err)
   119  	}
   120  	return nil
   121  }
   122  
   123  func (s *gelfLogger) Close() error {
   124  	return s.writer.Close()
   125  }
   126  
   127  func (s *gelfLogger) Name() string {
   128  	return name
   129  }
   130  
   131  // ValidateLogOpt looks for gelf specific log options gelf-address, &
   132  // gelf-tag.
   133  func ValidateLogOpt(cfg map[string]string) error {
   134  	for key := range cfg {
   135  		switch key {
   136  		case "gelf-address":
   137  		case "gelf-tag":
   138  		default:
   139  			return fmt.Errorf("unknown log opt '%s' for gelf log driver", key)
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  func parseAddress(address string) (string, error) {
   146  	if urlutil.IsTransportURL(address) {
   147  		url, err := url.Parse(address)
   148  		if err != nil {
   149  			return "", err
   150  		}
   151  
   152  		// we support only udp
   153  		if url.Scheme != "udp" {
   154  			return "", fmt.Errorf("gelf: endpoint needs to be UDP")
   155  		}
   156  
   157  		// get host and port
   158  		if _, _, err = net.SplitHostPort(url.Host); err != nil {
   159  			return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port")
   160  		}
   161  
   162  		return url.Host, nil
   163  	}
   164  
   165  	return "", nil
   166  }