github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/src/log/syslog/syslog.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build !windows,!nacl,!plan9
     6  
     7  package syslog
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"log"
    13  	"net"
    14  	"os"
    15  	"strings"
    16  	"sync"
    17  	"time"
    18  )
    19  
    20  // The Priority is a combination of the syslog facility and
    21  // severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
    22  // message from the FTP facility. The default severity is LOG_EMERG;
    23  // the default facility is LOG_KERN.
    24  type Priority int
    25  
    26  const severityMask = 0x07
    27  const facilityMask = 0xf8
    28  
    29  const (
    30  	// Severity.
    31  
    32  	// From /usr/include/sys/syslog.h.
    33  	// These are the same on Linux, BSD, and OS X.
    34  	LOG_EMERG Priority = iota
    35  	LOG_ALERT
    36  	LOG_CRIT
    37  	LOG_ERR
    38  	LOG_WARNING
    39  	LOG_NOTICE
    40  	LOG_INFO
    41  	LOG_DEBUG
    42  )
    43  
    44  const (
    45  	// Facility.
    46  
    47  	// From /usr/include/sys/syslog.h.
    48  	// These are the same up to LOG_FTP on Linux, BSD, and OS X.
    49  	LOG_KERN Priority = iota << 3
    50  	LOG_USER
    51  	LOG_MAIL
    52  	LOG_DAEMON
    53  	LOG_AUTH
    54  	LOG_SYSLOG
    55  	LOG_LPR
    56  	LOG_NEWS
    57  	LOG_UUCP
    58  	LOG_CRON
    59  	LOG_AUTHPRIV
    60  	LOG_FTP
    61  	_ // unused
    62  	_ // unused
    63  	_ // unused
    64  	_ // unused
    65  	LOG_LOCAL0
    66  	LOG_LOCAL1
    67  	LOG_LOCAL2
    68  	LOG_LOCAL3
    69  	LOG_LOCAL4
    70  	LOG_LOCAL5
    71  	LOG_LOCAL6
    72  	LOG_LOCAL7
    73  )
    74  
    75  // A Writer is a connection to a syslog server.
    76  type Writer struct {
    77  	priority Priority
    78  	tag      string
    79  	hostname string
    80  	network  string
    81  	raddr    string
    82  
    83  	mu   sync.Mutex // guards conn
    84  	conn serverConn
    85  }
    86  
    87  // This interface and the separate syslog_unix.go file exist for
    88  // Solaris support as implemented by gccgo. On Solaris you cannot
    89  // simply open a TCP connection to the syslog daemon. The gccgo
    90  // sources have a syslog_solaris.go file that implements unixSyslog to
    91  // return a type that satisfies this interface and simply calls the C
    92  // library syslog function.
    93  type serverConn interface {
    94  	writeString(p Priority, hostname, tag, s, nl string) error
    95  	close() error
    96  }
    97  
    98  type netConn struct {
    99  	local bool
   100  	conn  net.Conn
   101  }
   102  
   103  // New establishes a new connection to the system log daemon. Each
   104  // write to the returned writer sends a log message with the given
   105  // priority (a combination of the syslog facility and severity) and
   106  // prefix tag. If tag is empty, the os.Args[0] is used.
   107  func New(priority Priority, tag string) (*Writer, error) {
   108  	return Dial("", "", priority, tag)
   109  }
   110  
   111  // Dial establishes a connection to a log daemon by connecting to
   112  // address raddr on the specified network. Each write to the returned
   113  // writer sends a log message with the facility and severity
   114  // (from priority) and tag. If tag is empty, the os.Args[0] is used.
   115  // If network is empty, Dial will connect to the local syslog server.
   116  // Otherwise, see the documentation for net.Dial for valid values
   117  // of network and raddr.
   118  func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
   119  	if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
   120  		return nil, errors.New("log/syslog: invalid priority")
   121  	}
   122  
   123  	if tag == "" {
   124  		tag = os.Args[0]
   125  	}
   126  	hostname, _ := os.Hostname()
   127  
   128  	w := &Writer{
   129  		priority: priority,
   130  		tag:      tag,
   131  		hostname: hostname,
   132  		network:  network,
   133  		raddr:    raddr,
   134  	}
   135  
   136  	w.mu.Lock()
   137  	defer w.mu.Unlock()
   138  
   139  	err := w.connect()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	return w, err
   144  }
   145  
   146  // connect makes a connection to the syslog server.
   147  // It must be called with w.mu held.
   148  func (w *Writer) connect() (err error) {
   149  	if w.conn != nil {
   150  		// ignore err from close, it makes sense to continue anyway
   151  		w.conn.close()
   152  		w.conn = nil
   153  	}
   154  
   155  	if w.network == "" {
   156  		w.conn, err = unixSyslog()
   157  		if w.hostname == "" {
   158  			w.hostname = "localhost"
   159  		}
   160  	} else {
   161  		var c net.Conn
   162  		c, err = net.Dial(w.network, w.raddr)
   163  		if err == nil {
   164  			w.conn = &netConn{conn: c}
   165  			if w.hostname == "" {
   166  				w.hostname = c.LocalAddr().String()
   167  			}
   168  		}
   169  	}
   170  	return
   171  }
   172  
   173  // Write sends a log message to the syslog daemon.
   174  func (w *Writer) Write(b []byte) (int, error) {
   175  	return w.writeAndRetry(w.priority, string(b))
   176  }
   177  
   178  // Close closes a connection to the syslog daemon.
   179  func (w *Writer) Close() error {
   180  	w.mu.Lock()
   181  	defer w.mu.Unlock()
   182  
   183  	if w.conn != nil {
   184  		err := w.conn.close()
   185  		w.conn = nil
   186  		return err
   187  	}
   188  	return nil
   189  }
   190  
   191  // Emerg logs a message with severity LOG_EMERG, ignoring the severity
   192  // passed to New.
   193  func (w *Writer) Emerg(m string) error {
   194  	_, err := w.writeAndRetry(LOG_EMERG, m)
   195  	return err
   196  }
   197  
   198  // Alert logs a message with severity LOG_ALERT, ignoring the severity
   199  // passed to New.
   200  func (w *Writer) Alert(m string) error {
   201  	_, err := w.writeAndRetry(LOG_ALERT, m)
   202  	return err
   203  }
   204  
   205  // Crit logs a message with severity LOG_CRIT, ignoring the severity
   206  // passed to New.
   207  func (w *Writer) Crit(m string) error {
   208  	_, err := w.writeAndRetry(LOG_CRIT, m)
   209  	return err
   210  }
   211  
   212  // Err logs a message with severity LOG_ERR, ignoring the severity
   213  // passed to New.
   214  func (w *Writer) Err(m string) error {
   215  	_, err := w.writeAndRetry(LOG_ERR, m)
   216  	return err
   217  }
   218  
   219  // Warning logs a message with severity LOG_WARNING, ignoring the
   220  // severity passed to New.
   221  func (w *Writer) Warning(m string) error {
   222  	_, err := w.writeAndRetry(LOG_WARNING, m)
   223  	return err
   224  }
   225  
   226  // Notice logs a message with severity LOG_NOTICE, ignoring the
   227  // severity passed to New.
   228  func (w *Writer) Notice(m string) error {
   229  	_, err := w.writeAndRetry(LOG_NOTICE, m)
   230  	return err
   231  }
   232  
   233  // Info logs a message with severity LOG_INFO, ignoring the severity
   234  // passed to New.
   235  func (w *Writer) Info(m string) error {
   236  	_, err := w.writeAndRetry(LOG_INFO, m)
   237  	return err
   238  }
   239  
   240  // Debug logs a message with severity LOG_DEBUG, ignoring the severity
   241  // passed to New.
   242  func (w *Writer) Debug(m string) error {
   243  	_, err := w.writeAndRetry(LOG_DEBUG, m)
   244  	return err
   245  }
   246  
   247  func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
   248  	pr := (w.priority & facilityMask) | (p & severityMask)
   249  
   250  	w.mu.Lock()
   251  	defer w.mu.Unlock()
   252  
   253  	if w.conn != nil {
   254  		if n, err := w.write(pr, s); err == nil {
   255  			return n, err
   256  		}
   257  	}
   258  	if err := w.connect(); err != nil {
   259  		return 0, err
   260  	}
   261  	return w.write(pr, s)
   262  }
   263  
   264  // write generates and writes a syslog formatted string. The
   265  // format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
   266  func (w *Writer) write(p Priority, msg string) (int, error) {
   267  	// ensure it ends in a \n
   268  	nl := ""
   269  	if !strings.HasSuffix(msg, "\n") {
   270  		nl = "\n"
   271  	}
   272  
   273  	err := w.conn.writeString(p, w.hostname, w.tag, msg, nl)
   274  	if err != nil {
   275  		return 0, err
   276  	}
   277  	// Note: return the length of the input, not the number of
   278  	// bytes printed by Fprintf, because this must behave like
   279  	// an io.Writer.
   280  	return len(msg), nil
   281  }
   282  
   283  func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
   284  	if n.local {
   285  		// Compared to the network form below, the changes are:
   286  		//	1. Use time.Stamp instead of time.RFC3339.
   287  		//	2. Drop the hostname field from the Fprintf.
   288  		timestamp := time.Now().Format(time.Stamp)
   289  		_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s",
   290  			p, timestamp,
   291  			tag, os.Getpid(), msg, nl)
   292  		return err
   293  	}
   294  	timestamp := time.Now().Format(time.RFC3339)
   295  	_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
   296  		p, timestamp, hostname,
   297  		tag, os.Getpid(), msg, nl)
   298  	return err
   299  }
   300  
   301  func (n *netConn) close() error {
   302  	return n.conn.Close()
   303  }
   304  
   305  // NewLogger creates a log.Logger whose output is written to the
   306  // system log service with the specified priority, a combination of
   307  // the syslog facility and severity. The logFlag argument is the flag
   308  // set passed through to log.New to create the Logger.
   309  func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
   310  	s, err := New(p, "")
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	return log.New(s, "", logFlag), nil
   315  }