...

Source file src/pkg/log/syslog/syslog.go

     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	}
   316	

View as plain text