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