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

     1  // +build linux
     2  
     3  // Package syslog provides the logdriver for forwarding server logs to syslog endpoints.
     4  package syslog
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"log/syslog"
    10  	"net"
    11  	"net/url"
    12  	"os"
    13  	"path"
    14  	"strconv"
    15  	"strings"
    16  
    17  	"github.com/Sirupsen/logrus"
    18  	"github.com/docker/docker/daemon/logger"
    19  	"github.com/docker/docker/daemon/logger/loggerutils"
    20  	"github.com/docker/docker/pkg/urlutil"
    21  )
    22  
    23  const name = "syslog"
    24  
    25  var facilities = map[string]syslog.Priority{
    26  	"kern":     syslog.LOG_KERN,
    27  	"user":     syslog.LOG_USER,
    28  	"mail":     syslog.LOG_MAIL,
    29  	"daemon":   syslog.LOG_DAEMON,
    30  	"auth":     syslog.LOG_AUTH,
    31  	"syslog":   syslog.LOG_SYSLOG,
    32  	"lpr":      syslog.LOG_LPR,
    33  	"news":     syslog.LOG_NEWS,
    34  	"uucp":     syslog.LOG_UUCP,
    35  	"cron":     syslog.LOG_CRON,
    36  	"authpriv": syslog.LOG_AUTHPRIV,
    37  	"ftp":      syslog.LOG_FTP,
    38  	"local0":   syslog.LOG_LOCAL0,
    39  	"local1":   syslog.LOG_LOCAL1,
    40  	"local2":   syslog.LOG_LOCAL2,
    41  	"local3":   syslog.LOG_LOCAL3,
    42  	"local4":   syslog.LOG_LOCAL4,
    43  	"local5":   syslog.LOG_LOCAL5,
    44  	"local6":   syslog.LOG_LOCAL6,
    45  	"local7":   syslog.LOG_LOCAL7,
    46  }
    47  
    48  type syslogger struct {
    49  	writer *syslog.Writer
    50  }
    51  
    52  func init() {
    53  	if err := logger.RegisterLogDriver(name, New); err != nil {
    54  		logrus.Fatal(err)
    55  	}
    56  	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
    57  		logrus.Fatal(err)
    58  	}
    59  }
    60  
    61  // New creates a syslog logger using the configuration passed in on
    62  // the context. Supported context configuration variables are
    63  // syslog-address, syslog-facility, & syslog-tag.
    64  func New(ctx logger.Context) (logger.Logger, error) {
    65  	tag, err := loggerutils.ParseLogTag(ctx, "{{.ID}}")
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	proto, address, err := parseAddress(ctx.Config["syslog-address"])
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	facility, err := parseFacility(ctx.Config["syslog-facility"])
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	log, err := syslog.Dial(
    81  		proto,
    82  		address,
    83  		facility,
    84  		path.Base(os.Args[0])+"/"+tag,
    85  	)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	return &syslogger{
    91  		writer: log,
    92  	}, nil
    93  }
    94  
    95  func (s *syslogger) Log(msg *logger.Message) error {
    96  	if msg.Source == "stderr" {
    97  		return s.writer.Err(string(msg.Line))
    98  	}
    99  	return s.writer.Info(string(msg.Line))
   100  }
   101  
   102  func (s *syslogger) Close() error {
   103  	return s.writer.Close()
   104  }
   105  
   106  func (s *syslogger) Name() string {
   107  	return name
   108  }
   109  
   110  func parseAddress(address string) (string, string, error) {
   111  	if address == "" {
   112  		return "", "", nil
   113  	}
   114  	if !urlutil.IsTransportURL(address) {
   115  		return "", "", fmt.Errorf("syslog-address should be in form proto://address, got %v", address)
   116  	}
   117  	url, err := url.Parse(address)
   118  	if err != nil {
   119  		return "", "", err
   120  	}
   121  
   122  	// unix socket validation
   123  	if url.Scheme == "unix" {
   124  		if _, err := os.Stat(url.Path); err != nil {
   125  			return "", "", err
   126  		}
   127  		return url.Scheme, url.Path, nil
   128  	}
   129  
   130  	// here we process tcp|udp
   131  	host := url.Host
   132  	if _, _, err := net.SplitHostPort(host); err != nil {
   133  		if !strings.Contains(err.Error(), "missing port in address") {
   134  			return "", "", err
   135  		}
   136  		host = host + ":514"
   137  	}
   138  
   139  	return url.Scheme, host, nil
   140  }
   141  
   142  // ValidateLogOpt looks for syslog specific log options
   143  // syslog-address, syslog-facility, & syslog-tag.
   144  func ValidateLogOpt(cfg map[string]string) error {
   145  	for key := range cfg {
   146  		switch key {
   147  		case "syslog-address":
   148  		case "syslog-facility":
   149  		case "syslog-tag":
   150  		case "tag":
   151  		default:
   152  			return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
   153  		}
   154  	}
   155  	if _, _, err := parseAddress(cfg["syslog-address"]); err != nil {
   156  		return err
   157  	}
   158  	if _, err := parseFacility(cfg["syslog-facility"]); err != nil {
   159  		return err
   160  	}
   161  	return nil
   162  }
   163  
   164  func parseFacility(facility string) (syslog.Priority, error) {
   165  	if facility == "" {
   166  		return syslog.LOG_DAEMON, nil
   167  	}
   168  
   169  	if syslogFacility, valid := facilities[facility]; valid {
   170  		return syslogFacility, nil
   171  	}
   172  
   173  	fInt, err := strconv.Atoi(facility)
   174  	if err == nil && 0 <= fInt && fInt <= 23 {
   175  		return syslog.Priority(fInt << 3), nil
   176  	}
   177  
   178  	return syslog.Priority(0), errors.New("invalid syslog facility")
   179  }