Black Lives Matter. Support the Equal Justice Initiative.

Source file src/log/syslog/syslog.go

Documentation: log/syslog

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

View as plain text