syslog.go 3.7 KB

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