Home | History | Annotate | Download | only in 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 // +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 can not
     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 and prefix.
    106 func New(priority Priority, tag string) (w *Writer, err error) {
    107 	return Dial("", "", priority, tag)
    108 }
    109 
    110 // Dial establishes a connection to a log daemon by connecting to
    111 // address raddr on the specified network.  Each write to the returned
    112 // writer sends a log message with the given facility, severity and
    113 // tag.
    114 // If network is empty, Dial will connect to the local syslog server.
    115 func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
    116 	if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
    117 		return nil, errors.New("log/syslog: invalid priority")
    118 	}
    119 
    120 	if tag == "" {
    121 		tag = os.Args[0]
    122 	}
    123 	hostname, _ := os.Hostname()
    124 
    125 	w := &Writer{
    126 		priority: priority,
    127 		tag:      tag,
    128 		hostname: hostname,
    129 		network:  network,
    130 		raddr:    raddr,
    131 	}
    132 
    133 	w.mu.Lock()
    134 	defer w.mu.Unlock()
    135 
    136 	err := w.connect()
    137 	if err != nil {
    138 		return nil, err
    139 	}
    140 	return w, err
    141 }
    142 
    143 // connect makes a connection to the syslog server.
    144 // It must be called with w.mu held.
    145 func (w *Writer) connect() (err error) {
    146 	if w.conn != nil {
    147 		// ignore err from close, it makes sense to continue anyway
    148 		w.conn.close()
    149 		w.conn = nil
    150 	}
    151 
    152 	if w.network == "" {
    153 		w.conn, err = unixSyslog()
    154 		if w.hostname == "" {
    155 			w.hostname = "localhost"
    156 		}
    157 	} else {
    158 		var c net.Conn
    159 		c, err = net.Dial(w.network, w.raddr)
    160 		if err == nil {
    161 			w.conn = &netConn{conn: c}
    162 			if w.hostname == "" {
    163 				w.hostname = c.LocalAddr().String()
    164 			}
    165 		}
    166 	}
    167 	return
    168 }
    169 
    170 // Write sends a log message to the syslog daemon.
    171 func (w *Writer) Write(b []byte) (int, error) {
    172 	return w.writeAndRetry(w.priority, string(b))
    173 }
    174 
    175 // Close closes a connection to the syslog daemon.
    176 func (w *Writer) Close() error {
    177 	w.mu.Lock()
    178 	defer w.mu.Unlock()
    179 
    180 	if w.conn != nil {
    181 		err := w.conn.close()
    182 		w.conn = nil
    183 		return err
    184 	}
    185 	return nil
    186 }
    187 
    188 // Emerg logs a message with severity LOG_EMERG, ignoring the severity
    189 // passed to New.
    190 func (w *Writer) Emerg(m string) (err error) {
    191 	_, err = w.writeAndRetry(LOG_EMERG, m)
    192 	return err
    193 }
    194 
    195 // Alert logs a message with severity LOG_ALERT, ignoring the severity
    196 // passed to New.
    197 func (w *Writer) Alert(m string) (err error) {
    198 	_, err = w.writeAndRetry(LOG_ALERT, m)
    199 	return err
    200 }
    201 
    202 // Crit logs a message with severity LOG_CRIT, ignoring the severity
    203 // passed to New.
    204 func (w *Writer) Crit(m string) (err error) {
    205 	_, err = w.writeAndRetry(LOG_CRIT, m)
    206 	return err
    207 }
    208 
    209 // Err logs a message with severity LOG_ERR, ignoring the severity
    210 // passed to New.
    211 func (w *Writer) Err(m string) (err error) {
    212 	_, err = w.writeAndRetry(LOG_ERR, m)
    213 	return err
    214 }
    215 
    216 // Warning logs a message with severity LOG_WARNING, ignoring the
    217 // severity passed to New.
    218 func (w *Writer) Warning(m string) (err error) {
    219 	_, err = w.writeAndRetry(LOG_WARNING, m)
    220 	return err
    221 }
    222 
    223 // Notice logs a message with severity LOG_NOTICE, ignoring the
    224 // severity passed to New.
    225 func (w *Writer) Notice(m string) (err error) {
    226 	_, err = w.writeAndRetry(LOG_NOTICE, m)
    227 	return err
    228 }
    229 
    230 // Info logs a message with severity LOG_INFO, ignoring the severity
    231 // passed to New.
    232 func (w *Writer) Info(m string) (err error) {
    233 	_, err = w.writeAndRetry(LOG_INFO, m)
    234 	return err
    235 }
    236 
    237 // Debug logs a message with severity LOG_DEBUG, ignoring the severity
    238 // passed to New.
    239 func (w *Writer) Debug(m string) (err error) {
    240 	_, err = w.writeAndRetry(LOG_DEBUG, m)
    241 	return err
    242 }
    243 
    244 func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
    245 	pr := (w.priority & facilityMask) | (p & severityMask)
    246 
    247 	w.mu.Lock()
    248 	defer w.mu.Unlock()
    249 
    250 	if w.conn != nil {
    251 		if n, err := w.write(pr, s); err == nil {
    252 			return n, err
    253 		}
    254 	}
    255 	if err := w.connect(); err != nil {
    256 		return 0, err
    257 	}
    258 	return w.write(pr, s)
    259 }
    260 
    261 // write generates and writes a syslog formatted string. The
    262 // format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
    263 func (w *Writer) write(p Priority, msg string) (int, error) {
    264 	// ensure it ends in a \n
    265 	nl := ""
    266 	if !strings.HasSuffix(msg, "\n") {
    267 		nl = "\n"
    268 	}
    269 
    270 	err := w.conn.writeString(p, w.hostname, w.tag, msg, nl)
    271 	if err != nil {
    272 		return 0, err
    273 	}
    274 	// Note: return the length of the input, not the number of
    275 	// bytes printed by Fprintf, because this must behave like
    276 	// an io.Writer.
    277 	return len(msg), nil
    278 }
    279 
    280 func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
    281 	if n.local {
    282 		// Compared to the network form below, the changes are:
    283 		//	1. Use time.Stamp instead of time.RFC3339.
    284 		//	2. Drop the hostname field from the Fprintf.
    285 		timestamp := time.Now().Format(time.Stamp)
    286 		_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s",
    287 			p, timestamp,
    288 			tag, os.Getpid(), msg, nl)
    289 		return err
    290 	}
    291 	timestamp := time.Now().Format(time.RFC3339)
    292 	_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
    293 		p, timestamp, hostname,
    294 		tag, os.Getpid(), msg, nl)
    295 	return err
    296 }
    297 
    298 func (n *netConn) close() error {
    299 	return n.conn.Close()
    300 }
    301 
    302 // NewLogger creates a log.Logger whose output is written to
    303 // the system log service with the specified priority. The logFlag
    304 // argument is the flag set passed through to log.New to create
    305 // the Logger.
    306 func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
    307 	s, err := New(p, "")
    308 	if err != nil {
    309 		return nil, err
    310 	}
    311 	return log.New(s, "", logFlag), nil
    312 }
    313